2015-12-02 51 views
1

考虑以下两个代码示例:本地引用int是否提供任何好处?

void int_fun(int val) {} 

void another_fun() { 
    vector<int> test(1, 1); 
    const int& int_ref = test[0]; 
    int_fun(int_ref); 
} 

​​

这是真的,编译器可以实现int_ref如无操作,而在第二种情况下它会创建的测试[0副本]?或者复制也可以在第二种情况下进行优化,而这两个样本在性能上相当?

实例和问题是事实,有时代码的清晰性是有益的建立,将马上被传递给一些功能例如像在本地对象的动机。

是否使用基本类型(如int,布尔等)参考提供在这样的情况下,或参考值和任何益处是等效(或可能值是更好)?

+1

你可以看一下汇编代码。编译器可能会删除所有内容。 – juanchopanza

+0

为什么首先需要对变量进行额外引用?你可以写'int_fun(test [0])'。 – user463035818

+0

@ tobi303你是对的,它只是为了清晰起见 - 有时函数需要大量参数,每个参数的形式都是var.GetAnotherVar()。GetSomethingElse()。value(),所以它更容易分解很少 - 也许这只是个人喜好。 –

回答

2

在你给的情况下,我看不出有什么真正的优势。我怀疑大多数编译器会在大多数情况下为这两个编码生成相同的代码(至少启用了优化)。我认为引用使得这个意图更加清晰:真正的意图是将原始对象传递给函数,并且无论出于何种原因,您都只是创建并传递别名到该对象。

在稍有不同的情况下,有可能是行为上的真正的区别,但。最明显的一个是,如果函数接收的参考:

void int_fun(int &val); 

void another_fun() { 
    vector<int> test(1, 1); 
    int &int_val = test[0]; 
    int_fun(int_val); 
} 

在这种情况下,int_fun可能修改它收到的参考价值,因此,如果您创建了一个基准,然后传递通过引用,引用将被折叠,所以函数可以修改数组中的值(但是如果您创建了一个副本,然后通过引用传递它,副本将被修改而不是原来的)。

+0

谢谢。我用const更新了这个问题,以避免这种模糊性。 –

-1

使用当地的参考价值有时是有用的,即使是基本类型,如果他们在里面更复杂的类型,这样就可以使用该引用作为一个读/写快捷方式到真正的变量。这里的诀窍是部分。如果不需要修改变量,那么复制该值也是一样好。

举例来说,一种更复杂代码:

void fun() 
{ 
    vector<int> test; 
    //fill test with a lot of values 
    for (int i=0; i < test.size(); ++i) 
    { 
     int &x = test[i]; //<-- handy reference! 
     if (x > i) 
      ++x; 
     else if (x < i) 
      --x;     
    } 
} 

如果你想要写同样的逻辑,而不引用,你必须使用vector::operator[](int)至少两次!

更改vectormap和东西更有意思:

void fun() 
{ 
    map<string, int> keys; 
    //fill keys 
    int &x = keys["foo"]; 
    ++x; 
} 
+0

这回答标题中的问题时,它的字面意思,但OP是要求可能的性能差异。 – user463035818

+0

@ tobi303:如果您注意到您正在将调用保存到外部的,可能很贵的函数(如'map :: operator []'),则性能差异很明显。 – rodrigo

+0

您正在编写自己的示例,在OP代码中,既没有地图,也没有两个版本在调用“外部”函数时的不同。 – user463035818

0

代码清晰。对于非平凡的代码,有时使用对容器成员的引用会产生更清晰的代码。另外,我可以在传递一个智能指针函数的时候这样做。在函数内部,我将通过将它分配给一个引用来解引用它,并在整个函数中使用该引用。 这样做会使它更清晰并避免复制对象。对于内置类型,我只是使用一个赋值并避免引用。

如果你真的需要的每一次表现,那么你可以看看产生汇编语言代码。之后,对其进行描述。

2

因为在现代硬件上制作原始单个拷贝的代价非常低,所以您显示的代码示例是微型优化(或者微型优化,无论情况如何)。对基元进行引用也是一样的。与调用任何感兴趣的函数的成本相比,成本会降低。

但是,对示例的一个非常简单的更改演示了在对基元的引用变得有益时的情况,因为您可以将它们分配回去。

这里就是你们的榜样修改的地方使用std::map<std::string,int>std::vector<int>

std::map<std::string,int> cache; 

int compute_new(int old) { 
    return old+1; 
} 

void fun_with_ref(const std::string& key) { 
    int& int_ref = cache[key]; 
    int_ref = compute_new(int_ref); 
} 

void fun_with_val(const std::string& key) { 
    int int_val = cache[key]; 
    cache[key] = compute_new(int_val); 
} 

注意如何fun_with_ref执行由key单个查找,而fun_with_val需要两个查找。 std::map<std::string,ing>的访问时间增长为O(日志 N),因此当地图增长到较大时节省可能变得很重要。

A quick micro-benchmark显示使用具有1,000,000个条目的映射的引用的代码几乎是使用值的代码的两倍。

vector<string> keys; 
for (int i = 0 ; i != 1000000 ; i++) { 
    auto key = to_string(i); 
    cache[key] = i; 
    keys.push_back(key); 
} 
auto ms1 = duration_cast<milliseconds>(
    system_clock::now().time_since_epoch() 
).count(); 
for (const string& key : keys) { 
    fun_with_ref(key); 
} 
auto ms2 = duration_cast<milliseconds>(
    system_clock::now().time_since_epoch() 
).count(); 
for (const string& key : keys) { 
    fun_with_val(key); 
} 
auto ms3 = duration_cast<milliseconds>(
    system_clock::now().time_since_epoch() 
).count(); 
cout << "Ref: " << (ms2-ms1) << endl; 
cout << "Val: " << (ms3-ms2) << endl; 

输出:

Ref: 557 
Val: 1064 
相关问题