关于c ++:指针与参考

Pointer vs. Reference

在给函数提供原始变量时,最好的做法是:

1
2
3
4
5
6
unsigned long x = 4;

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

或:

1
2
3
4
void func2(unsigned long* val) {
     *val = 5;
}
func2(&x);

值班驾驶员:有没有理由选择一个而不是另一个?


我的经验法则是:

如果要使用指针进行指针算术(例如,增加指针地址以逐步通过数组),或者如果必须传递空指针,请使用指针。

否则使用引用。


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

  • 和其他地方一样,总是正确的。

    • 注:这意味着,除其他外,只有out值(见第3项)和value传递的值(见第4项)可能缺少const说明符。
  • 仅当值0/null是当前上下文中的有效输入时,才按指针传递值。

    • 理由1:作为一个调用者,你会看到你传递的信息必须处于可用状态。

    • 理由2:正如所说,你知道任何东西都处于可用状态。因此,不需要对该值进行空检查或错误处理。

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

  • 如果函数参数是out值,则通过引用传递它。

    • 理由:我们不想破坏第2项……
  • 只有当值是pod(普通的旧数据结构)或足够小(内存方面)或以其他方式足够便宜(时间方面)复制时,才选择"pass-by-value"而不是"pass-by-const-reference"。

    • 理由:避免不必要的复制品。
    • 注:足够小和足够便宜不是绝对可测量的。

  • 这最终是主观的。到目前为止,讨论是有用的,但我认为没有正确或决定性的答案。很大程度上取决于风格指南和您当时的需求。

    虽然指针具有一些不同的功能(无论是否可以为空),但输出参数的最大实际差异是纯语法。谷歌的C++风格指南(http://谷歌,Github.Io/StultGueld/CppGueld.html引用引用参数),例如,只为输出参数指定指针,只允许引用const。推理是可读性的一种:具有值语法的东西不应该具有指针语义意义。我不是说这必然是对的或错的,但我认为这里的重点是,这是一个风格问题,而不是正确性问题。


    如果要修改变量的值,应该传递一个指针。尽管从技术上讲传递引用或指针是相同的,但是在用例中传递指针更易读,因为它"宣传"了值将被函数更改的事实。


    如果您有一个参数,可能需要在其中指示缺少值,那么通常的做法是将该参数设置为指针值并传入空值。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // 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);
    }

    指针

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

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

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

      1
      2
      3
         int nVar = 7;
         int* ptrVar = &nVar;
         int nVar2 = *ptrVar;

    参考文献

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

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

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

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

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

    • 不能有空引用。

    • const引用可以引用const int。它是用一个值为const的临时变量完成的。

      1
      2
      3
      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

    enter image description here

    enter image description here


    可以时使用引用,必要时使用指针。从C++ FAQ:"我应该什么时候使用引用,什么时候我应该使用指针?"


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


    引用是一个隐式指针。基本上,您可以更改引用指向的值,但不能更改引用指向其他对象。所以我的2分是,如果你只想改变一个参数的值,作为一个引用传递它,但是如果你需要改变这个参数来指向一个不同的对象,用一个指针传递它。


    指针:

    • 可以指定nullptr(或NULL)。
    • 在调用站点,如果您的类型不是指针本身,则必须使用&,明确表示您正在修改对象。
    • 指针可以反弹。

    参考文献:

    • 不能为空。
    • 一旦绑定,就无法更改。
    • 呼叫者不需要明确使用&。有时会考虑不好,因为您必须转到函数的实现来查看您的参数已修改。


    传递常量引用,除非您希望更改/保留要传递的内容。

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

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


    引用类似于指针,只是不需要使用前缀?访问引用引用的值。此外,在初始化后不能使引用引用引用其他对象。

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

    有关更多信息,请参见"Bjarne Stroustrup"(2014)页11-12的"C++之旅"