指针与参考


256

给函数一个原始变量以供使用时,更好的做法是:

unsigned long x = 4;

void func1(unsigned long& val) {
     val = 5;            
}
func1(x);

要么:

void func2(unsigned long* val) {
     *val = 5;
}
func2(&x);

爱荷华州:有什么理由选择一个?


1
引用当然很有价值,但是我来自C,那里到处都有指针。必须先精通指针才能理解引用的价值。
杰伊D

这如何满足目标,例如函数式编程的引用透明性?如果您始终希望函数返回新对象并且从不内部改变状态,尤其是不传递给函数的变量,该怎么办?有没有办法将此概念仍与C ++之类的指针和引用一起使用。(请注意,我假设某人已经具有引用透明性的目标。我对谈论它是否是一个好目标不感兴趣。)
2013年

优先参考。用户指针,如果您别无选择。
Ferruccio 2014年

Answers:


285

我的经验法则是:

如果要使用指针进行算术运算(例如,增加指针地址以遍历数组)或必须传递NULL指针,请使用指针。

否则使用引用。


10
关于指针为NULL的优秀点。如果有指针参数,则必须显式检查它是否为NULL,或者搜索该函数的所有用法以确保它永远不会为NULL。不需要进行参考。
理查德·科登,

26
用算术解释你的意思。新用户可能不了解您要调整指针指向的内容。
马丁·约克

7
马丁,算术上,我的意思是您将指针传递给结构,但知道它不是简单的结构,而是它的数组。在这种情况下,您可以使用[]对其进行索引,也可以使用指针上的++ /-进行算术运算。简而言之,这就是差异。
Nils Pipenbrinck

2
马丁,您只能直接使用指针执行此操作。不带参考。当然,你可以采取一个指针引用,并做同样的事情在实践中,但如果你这样做你非常肮脏代码结束..
尼尔斯Pipenbrinck

1
那么多态性(例如Base* b = new Derived())呢?这似乎是没有指针就无法处理的情况。
克里斯·雷德福

72

我真的认为您将从建立以下函数调用编码准则中受益:

  1. 与在所有其他地方一样,总是const正确的。

    • 注意:这意味着,除其他事项外,只有出值(请参阅第3条)和按值传递的值(请参见第4条)可以缺少说明const符。
  2. 仅当值0 / NULL是当前上下文中的有效输入时,才通过指针传递值。

    • 基本原理1:作为呼叫者,您会看到传入的任何内容都必须处于可用状态。

    • 理由2:由于,你知道,无论发生什么事的处于可用状态。因此,不需要对该值进行NULL检查或错误处理。

    • 基本原理3:基本原理1和2将由编译器强制执行。如果可以,请始终在编译时捕获错误。

  3. 如果函数参数是一个超值,则通过引用传递它。

    • 理由:我们不想破坏项目2 ...
  4. 仅当值是POD(普通旧数据结构)或足够小(按存储方式)或以其他便宜方式(按时间)复制时,才选择“按值传递”而不是“按常量引用传递” 。

    • 理由:避免不必要的复制。
    • 注意:足够小足够便宜并不是绝对的可衡量的指标。

它在以下情况下缺少准则:...“何时使用const&” ...准则2应该写为“用于[in]值,只有在NULL有效的情况下才通过指针传递。否则,请使用const引用(或对于”小”对象,副本)或引用(如果它是[out]值)。我正在监视此帖子,以可能添加+
1。– paercebal

项目1涵盖您描述的情况。
约翰·杰瑞尔

如果它不是默认可构造的,则通过引用传递一个外参数有点困难。这在我的代码中很常见-拥有创建该外部对象的函数的全部原因是因为它并不简单。
MSalters

@MSalters:如果要在函数内部分配内存(我认为这是您的意思),那么为什么不只返回指向已分配内存的指针呢?
Kleist

@Kleist:代表@MSalters的原因很多。一种是您可能已经分配了要填充的内存,例如pre-size std::vector<>
Johann Gerell 2011年

24

这最终最终成为主观的。到目前为止的讨论是有用的,但是我认为对此没有正确或决定性的答案。很大程度上取决于样式准则和您当时的需求。

尽管指针具有一些不同的功能(某些东西是否可以为NULL),但输出参数的最大实际差异是语法。例如,Google的C ++样式指南(https://google.github.io/styleguide/cppguide.html#Reference_Arguments)仅强制要求输出参数的指针,并且仅允许const引用。推理是可读性之一:具有值语法的内容不应具有指针语义。我并不是说这必然是对还是错,但是我认为这里的重点是样式问题,而不是正确性。


引用具有值语法但具有指针语义的含义是什么?
埃里克·安德鲁·刘易斯

看起来您正在传递副本,因为“按引用传递”部分仅在函数定义(值语法)中可见,但是您并未复制传递的值,实际上是在内部传递了一个指针,这允许修改您的价值的功能。
phant0m

一个人不要忘记Google C ++样式指南受到了极大的憎恶。
Deduplicator

7

如果要修改变量的值,则应传递一个指针。即使从技术上讲传递引用或指针在技术上是相同的,但在用例中传递指针更易读,因为它“宣告”值将由函数更改的事实。


2
如果您遵循Johann Gerell的准则,则非常量引用也将通告可更改的变量,因此指针在此处没有这种优势。
2011年

4
@AlexanderKondratskiy:您错过了要点……您无法在调用站点立即看到被调用函数是否接受参数作为constconst引用,但是您可以查看参数是否通过ala &xvs. 传递x,并使用该约定编码是否可以修改参数。(这就是说,有时候您想传递一个const指针,因此惯例只是一个提示。可以争论的是,怀疑某些东西在不被修改时可能会被修改的危险要比认为在某件事不会被修改时要危险的多。 ....)
Tony Delroy

5

如果您有一个可能需要指示不存在值的参数,通常的做法是使该参数成为指针值并传入NULL。

在大多数情况下(从安全角度而言),更好的解决方案是使用boost :: optional。这允许您通过引用和返回值传递可选值。

// Sample method using optional as input parameter
void PrintOptional(const boost::optional<std::string>& optional_str)
{
    if (optional_str)
    {
       cout << *optional_str << std::endl;
    }
    else
    {
       cout << "(no string)" << std::endl;
    }
}

// Sample method using optional as return value
boost::optional<int> ReturnOptional(bool return_nothing)
{
    if (return_nothing)
    {
       return boost::optional<int>();
    }

    return boost::optional<int>(42);
}


4

指针

  • 指针是保存内存地址的变量。
  • 指针声明由基本类型,*和变量名组成。
  • 指针可以指向生命周期中的任意数量的变量
  • 当前未指向有效内存位置的指针的值为null(这是零)

    BaseType* ptrBaseType;
    BaseType objBaseType;
    ptrBaseType = &objBaseType;
  • &是一元运算符,返回其操作数的内存地址。

  • 解引用运算符(*)用于访问指针所指向的变量中存储的值。

       int nVar = 7;
       int* ptrVar = &nVar;
       int nVar2 = *ptrVar;

参考

  • 引用(&)类似于现有变量的别名。

  • 引用(&)就像是自动取消引用的常量指针。

  • 它通常用于函数参数列表和函数返回值。

  • 创建引用时,必须对其进行初始化。

  • 一旦将引用初始化为一个对象,就不能将其更改为引用另一个对象。

  • 您不能有NULL引用。

  • const引用可以引用const int。这是通过使用const值的临时变量完成的

    int i = 3;    //integer declaration
    int * pi = &i;    //pi points to the integer i
    int& ri = i;    //ri is refers to integer i – creation of reference and initialization

在此处输入图片说明

在此处输入图片说明


3

引用是隐式指针。基本上,您可以更改引用所指向的值,但不能更改引用以指向其他内容。所以我的2美分是,如果您只想更改参数的值,请将其作为引用传递,但是如果您需要更改参数以指向其他对象,则使用指针将其传递。


3

考虑一下C#的out关键字。编译器要求方法的调用者将out关键字应用于任何out args,即使它已经知道它们是否存在。这旨在增强可读性。尽管对于现代IDE,我倾向于认为这是语法(或语义)突出显示的工作。


错别字:语义,而非交际;+1我同意加

2

通过const引用传递,除非您有理由要更改/保留要传递的内容。

在大多数情况下,这将是最有效的方法。

确保在不希望更改的每个参数上使用const,因为这不仅可以防止您在函数中做一些愚蠢的事情,而且还可以向其他用户很好地指示函数对传入值的作用。这包括当您只想更改指向的内容时使指针为const。


2

指标:

  • 可以分配nullptr(或NULL)。
  • 在呼叫站点,您必须使用 &如果您的类型本身不是指针,则它,从而明确地在修改对象。
  • 指针可以反弹。

参考文献:

  • 不能为null。
  • 一旦绑定,就无法更改。
  • 呼叫者无需明确使用&。有时这被认为是不好的,因为您必须转到函数的实现以查看您的参数是否被修改。

一个小点,以那些不知道谁:nullptr或NULL是一个简单的0 stackoverflow.com/questions/462165/...
谢尔盖·费多罗夫


0

引用类似于指针,不同之处在于您不需要使用前缀∗来访问引用所引用的值。同样,在初始化之后不能进行引用以引用不同的对象。

引用对于指定函数参数特别有用。

有关更多信息,请参阅“ Bjarne Stroustrup”的“ A C ++之旅”(2014)第11-12页

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.