【问题标题】:What are the differences between parameter definitions as (type& name), and (type* name)?参数定义为 (type& name) 和 (type* name) 有什么区别?
【发布时间】:2010-10-31 05:47:18
【问题描述】:

一个非常基本的问题,但仍然很高兴听到 C++ 专家的意见。

在 C++ 中有两种相当相似的方式来声明引用参数。

1) 使用“星号”:

void DoOne(std::wstring* iData);

2) 使用“&”:

void DoTwo(std::wstring& iData);

每种方法的含义是什么? 在任何情况下都有什么陷阱吗?

奖励 #1:在 #1 和 #2 中调用方法的正式方式是什么?它们都被称为“按引用”吗?

奖励 #2:故意使用 std::wstring。在每种情况下对标准库类有何影响?

【问题讨论】:

标签: c++ argument-passing


【解决方案1】:

规则一:如果 NULL 是函数上下文中函数参数的有效值,则将其作为指针传递,否则作为引用传递。

理由,如果它不能(不应该!)永远是 NULL,那么不要让自己经历检查 NULL 的麻烦。

【讨论】:

    【解决方案2】:

    如果您编写一个通过指针获取变量的函数,您很可能必须检查指针是否有效(例如,不为 NULL),否则您将面临程序崩溃的风险。

    【讨论】:

    • “你有程序崩溃的风险”。 strcpy 等标准函数不检查空值。他们认为是他们的调用者冒着崩溃的风险(通过传递一个无效的值),而不是他们。
    【解决方案3】:

    当你说:

    他们每个人的结果都相当 类似:对一个对象的引用 内存最终在方法的 范围。好像没有严格的 其中任何一个的内存要求。

    考虑:

    void f( int * p1 ) {
       int ** p2 = & p1;
    }
    

    这里 p1 有一个明确的“内存要求”——它必须存在并且我必须能够获取它的地址。对比一下

    void f( int & r ) }
       int * p = & r;
    }
    

    这里的 r 没有它自己的存在,它只是一个引用。我拿它的时候 address 我正在获取 r 所指事物的地址。

    您对 NULL 指针的评论也是错误的。取消引用 NULL 指针会导致未定义的行为 - 这可能会也可能不会导致崩溃。

    【讨论】:

    • 你的意思是说在 C++ 中不存在“通过指针”这样的东西吗?虽然你是对的,但我认为问题在于何时使用指针或引用。
    • 不,我不是这个意思。
    【解决方案4】:

    #1 使用指针参数('传递指针'),#2 使用引用参数('通过引用传递')。它们非常相似,但请注意,两种情况下的调用代码看起来不同:

    std::wstring s;
    
    DoOne(&s); // pass a pointer to s
    DoTwo(s); // pass s by reference
    

    有些人更喜欢#1,使用通过指针传递的约定表示函数可能会更改 s 的值(即使任何一个函数都可以)。其他人(包括我自己)更喜欢 #2,因为通过引用传递不允许传递 NULL。

    通过const 指针或引用传递时还有另一个重要区别。临时变量只能传递给 const 引用参数:

    void ByConstPointer(const std::wstring&);
    void ByConstReference(const std::wstring*);
    
    void test()
    {
      ByConstPointer(&std::wstring(L"Hello")); // error: cannot take address of temporary
      ByConstReference(std::wstring(L"Hello")); // fine
    }
    

    【讨论】:

      【解决方案5】:

      在 DoOne 中,可以将 iData 分配为 NULL。如果你在调用 DoOne 后使用它,应用程序会崩溃。

      类似

      void DoOne(std::wstring* iData)
      {
         //Use iData
         delete iData;
         iData = NULL;
      }
      

      {
          std::wstring* pData = new std::wstring();
          DoOne(pData);
          pData->someFunction(); //Crash
      }
      

      【讨论】:

      • 它会因为对象被删除而不是因为指针被分配给NULL而崩溃。
      【解决方案6】:

      我不会称自己为 C++ 人物(除了我的简历),但我会说;除非有任何将参数作为指针传递的用途(即函数要检查空值),否则请始终使用引用。

      这也适用于返回对象的函数,返回一个指针以某种方式告诉类的用户它可能为空。

      【讨论】:

        【解决方案7】:

        在编写示例时,我想出了自己的答案。除了下面还有什么?

        它们中的每一个的结果都非常相似:对内存中对象的引用最终在方法的范围内。它们中的任何一个似乎都没有严格的内存要求。对象可以在堆栈上或堆上。

        在堆栈的情况下,每个方法都将像这样调用:

        {
            std::wstring data;
            DoOne(&data);
            DoTwo(data);
        }
        

        然而,当涉及到堆时,第二种方法将要求对象在调用方法之前必须存在。如果对象不存在,调用者会引发异常,而不是被调用者。

        {
            std::wstring* pData = new std::wstring();
            DoOne(pData);
            DoTwo(*pData);
        }
        

        在上述情况下,如果出现内存不足的情况并且 pData 最终为 NULL,则崩溃会在 DoTwo 之前发生,但 DoOne 会吞下 NULL 并可能在一段时间后崩溃。

        【讨论】:

        • 有!每个方法是如何正式调用的?
        • DoOne 是按引用传递,DoTwo 是按指针传递。引用不能为 NULL,它由编译器强制执行。所以你的说法“如果对象不存在,调用者会导致异常”是错误的。此外,new 不能返回 NULL。如果失败,则抛出异常。所以你不必担心。在大多数情况下,首选传递引用。
        • 如果您(无效地)取消引用空指针,编译器不需要停止引用为空。我希望大多数编译器在使用对象时会在被调用者中发生崩溃。其他任何事情都需要编译器在调用者中插入一个空检查,这可能会很昂贵。但是你是对的,new 不会返回 null,除非你使用的是像 Symbian 这样的预标准编译器。 nothrow new 可以说明这个问题。
        • pData 永远不会为 NULL。在内存不足的情况下,new 会抛出异常。
        • 如果您指示编译器禁用异常,pData 很可能为 0。取消对 NULL 指针的引用不是错误,但对它做任何事情都是未定义的。通常,它的作用与 NULL 指针相同。
        猜你喜欢
        • 1970-01-01
        • 2023-03-30
        • 2011-01-28
        • 2015-04-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-25
        相关资源
        最近更新 更多