2013-07-12 53 views
0

关于“我什么时候使用引用和指针?”有很多问题。他们让我困惑了一下。我以为参考资料不会记忆,因为它只是地址。C++ - 参考,参数指针

现在,我做了一个简单的Date类,并给他们的代码审查的社区。他们告诉我不要在下面的例子中使用参考。但为什么?
有人告诉我它会分配一个指针分配的相同内存。这与我所学到的相反。

class A{ 
int a; 
public: 
    void setA(const int& b) { a = b; } /* Bad! - But why?*/ 
}; 

class B{ 
int b; 
public: 
    void setB(int c) { b = c; } /* They told me to do this */ 
}; 

那么我什么时候在参数中使用引用或指针,何时只是简单的复制呢?没有我的例子中的参考,常量是不必要的?

+4

“他们告诉我不要用在下面的示例参考。但是,为什么?”他们不是说为什么吗?没有解释的理由,获取代码审查意见并不是很有用。你不能问他们为什么? –

回答

4

如果您拥有像intlong这样的轻量级类型,则应使用按值传递,因为使用引用不会产生额外成本。但是,当你通过一些重型,你应该使用参考

+0

简单易懂。谢谢(虽然有点晚...嘿嘿) – Davlog

8

它不保证是坏的。但在这个特定情况下,它是不必要的。

在许多(或大多数)上下文中,引用是作为伪装指针来实现的。你的例子恰好是这些情况之一。假设函数没有被内联,参数b将作为指针在“引擎盖下”实现。所以,你真正通过setA在第一个版本中是指针int,即提供间接访问您的参数值。在第二个版本中,您立即传递int,即提供直接访问您的参数值。

哪个更好,哪个更差?那么,在许多情况下,指针的大小比int大,这意味着第一个变体可能会传递更大量的数据。这可能被认为是“不好的”,但是由于两种数据类型通常都适合硬件字的大小,所以它可能没有明显的差别,特别是如果参数在CPU寄存器中传递的话。

此外,为了读b是变相的指针,你必须取消引用函数内。从性能角度来看,这也是“不好的”。

这些是人们希望通过值传递任何小尺寸参数(小于或等于指针大小)的正式原因。对于参数或更大的尺寸,传递const引用变成一个更好的主意(假设你没有明确要求副本)。

然而,在大多数情况下,一个功能简单很可能会被内联,这将完全消除两个变体之间的差别,不管你使用的参数类型。


const在第二个变体中是不必要的事情是一个不同的故事。 const在第一个变体中有两个重要用途:

1)它阻止您修改参数值,从而保护实际参数不被修改。如果引用不是const,则可以修改引用参数,从而修改参数。

2)它允许你使用rvalues作为参数,例如,致电some_obj.setA(5)。没有那个const这样的呼叫是不可能的。

在第二个版本中这两个都不是问题。没有必要保护修改后的实际参数,因为参数是该参数的本地副本。不管你对参数做什么,实际的参数都将保持不变。不管参数是否被声明为const,您都可以使用rvalues作为参数SetA

因此,人们通常不会对通过值传递的参数使用顶级const限定符。但是,如果您确实声明const,它将阻止您修改函数内的本地b。有些人确实喜欢这样做,因为它强制执行适度流行的“不要修改原始参数值”约定,因此有时可能会在参数声明中看到顶级const限定符。

+0

不错的答案,但我建议将“引用只是一个指针”替换为“引用只是使用指针实现”。如果对这两者不太了解,最终他们会让“指针是数组” - 样式“引用是指针”的错误。 – 2013-07-12 18:13:51

1

我同意审查。这里的原因:

A(const或非const)引用一个简单的小类型,如int将更加复杂(在指令数量)。这是因为呼叫代码必须将参数的地址传递到setA,然后在setA内,必须从存储在b中的地址取消该值。在b是普通的int的情况下,它仅复制该值本身。所以在保存中至少有一个内存引用的步骤。这在大型程序的长运行时间中可能没有太大的区别,但如果您在任何地方不断增加一个额外的循环,那么它会很快加起来明显变慢。

我看了一下一段代码了一句这样的:

class X 
{ 
    vector v; 
    public: 
    ... 
    void find(int& index, int b); 
    .... 
} 

bool X::find(int &index, int b) 
{ 
    while(v[index] != b) 
    { 
     if (index == v.size()-1) 
     { 
      return false; 
     } 
     index++; 
    } 
    return true; 
} 

这段代码重写:

bool X::find(int &index, int b) 
{ 
    int i = index; 
    while(v[i] != b) 
    { 
     if (i == v.size()-1) 
     { 
      index = i; 
      return false; 
     } 
     i++; 
    } 
    index = i; 
    return true; 
} 

意味着这个功能从总量的30%去了一些代码的执行相当多,称为find,大约相同测试的执行时间的5%。因为编译器将i放在一个寄存器中,并且只有在完成搜索时才更新参考值。

0

引用是作为指针来实现的(这不是必需的,但我相信它是普遍真实的)。因为你只是传递一个“int”,所以传递指向该int的指针将需要传递相同数量的空间(相同或多个寄存器,或相同或更多的堆栈空间,取决于你的架构),所以没有节省。此外,现在您必须对该指针取消引用,这是一个额外的操作(并且几乎肯定会导致您进入内存,而根据您的体系结构,您可能不需要对第二个内存执行操作)。

现在,如果你传递的比一个int大得多,那么第一个可能会更好,因为你只传递一个指针。 [注意,有些情况下,即使是非常大的物体,通过价值传递仍然是有意义的。无论如何,这些情况通常是当你计划创建自己的副本时。在这种情况下,最好让编译器进行复制,因为总体方法可能会提高它的优化能力。这些情况非常复杂,我认为如果你问这个问题,你应该在尝试解决它们之前更多地学习C++。虽然它们确实有趣的阅读。]

0

传递基元作为const引用不会为您节省任何东西。指针和int使用相同数量的内存。如果你传递一个const引用,机器将不得不为一个指针分配内存并复制指针地址,这与分配和复制整数具有相同的成本。如果Date类使用单个64位整数(或double)来存储日期,则不需要使用const-reference。但是,如果Data类变得更复杂并存储其他字段,则通过const-reference传递Date对象的成本应低于按值传递Date对象的成本。