【问题标题】:When to use const and const reference in function args?何时在函数 args 中使用 const 和 const 引用?
【发布时间】:2011-04-27 09:36:51
【问题描述】:

在编写具有传递给它的 args 的 C++ 函数时,根据我的理解,如果您可以保证对象不会被更改,则应始终使用 const;如果指针不会更改,则应始终使用 const 指针。

什么时候建议这种做法?

什么时候使用 const 引用?例如,与仅通过指针传递它相比有什么优势?

void MyObject::Somefunc(const std::string& mystring) 这个void MyObject::Somefunc(const std::string& mystring) 如果一个字符串实际上已经是一个不可变对象,那么拥有一个 const 字符串有什么意义?

【问题讨论】:

  • 原因 1:字符串不是不可变的。原因 2:能够传递字符串文字或 c 字符串或右值。如果您通过简单的引用,则不会应用任何转换,也不能将临时对象作为参数传递

标签: c++ arguments constants


【解决方案1】:

一般规则是,尽可能使用const,仅在必要时省略它。 const 可以使编译器优化并帮助您的同行了解您的代码打算如何使用(并且编译器会发现可能的误用)。

就您的示例而言,字符串在 C++ 中不是不可变的。如果您将非const 对字符串的引用传递给函数,则该函数可能会修改它。 C++ 语言中没有内置不变性的概念,您只能使用封装和const 来模拟它(尽管这永远不会是防弹的)。

在思考了@Eamons 的评论并阅读了一些内容后,我同意优化不是使用const 的主要原因。主要原因是代码正确。

【讨论】:

  • 我不认为编译器实际上可以基于 const 注释优化任何东西 - const 并不意味着不变性,只是缺乏在此范围内更改的意图。
  • +1 用于提醒字符串不是不可变的 - 这非常重要!
  • @Eamon: Read this 了解const 的一些优化优缺点。
【解决方案2】:

const 引用相对于 const 指针的主要优点如下:很明显参数是必需的,不能为 NULL。 反之亦然,如果我看到一个 const 指针,我立即假设它不是引用的原因是参数可能为 NULL。

【讨论】:

    【解决方案3】:

    这些问题基于一些不正确的假设,因此没有真正的意义。

    std::string 不模拟不可变的字符串值。它为可变值建模。

    没有“常量引用”这样的东西。有对const 对象的引用。区别很微妙,但很重要。

    函数参数的顶级const 仅对函数实现有意义,而不是纯声明(编译器忽略它)。它不会告诉调用者任何事情。这只是对实施的限制。例如。 int const 在函数的纯声明中作为参数类型几乎毫无意义。但是,std::string const& 中的const 不是顶级的。

    通过引用const 来避免低效的数据复制。通常,对于将数据传递给函数的参数,您通过值传递小项(例如int),并通过引用const 传递可能更大的项。在机器代码中,对const 的引用可能会被优化掉,或者它可能被实现为指针。例如,在 32 位 Windows 中,int 是 4 个字节,而指针是 4 个字节。因此,参数类型 int const& 不会减少数据复制,但可以使用头脑简单的编译器引入额外的间接,这意味着效率稍低——因此有小/大的区别。

    干杯,

    【讨论】:

    • 人们经常使用术语“const reference”,意思是“对 const 对象的引用”......我会说前者 - 鉴于它没有其他可能的含义或解释 - 是一种非正式的讨论方式后者,而不是在不存在的东西和存在的东西之间有一个微妙的区别;-)。
    • 您可以将const 引用到非const 对象。 const-ness(甚至是一个词?)是引用的属性,而不是对象的属性。
    • @Tony:这里术语准确的问题是非常现实的。 “const 指针”与“指向 const 的指针”非常不同(即使某些 Microsoft 程序员不理解这一点)。而对于 MSVC 'T& const'(一个“const 引用”),编译器会惊呼“使用过时的错误:引用上的限定符被忽略”,所以虽然这对某些人来说显然是不可想象的,但有一次它显然被这个编译器接受了——因此,IMO 与此区别非常相关。干杯,
    • @Alf:假设你有一个函数,它把const 对象的引用作为参数。现在您使用非const 对象作为参数调用该函数。它是否使对象const?不,引用使它在函数中显示为const,但对象本身不是const。我不是 C++ 标准和定义方面的专家,我只是觉得你的术语有点误导。
    • @Alf:非常正确,但最终我会直截了当地说,我认为您是在表达自己的观点,而不是代表 C++ 专家社区的任何共识或最佳实践- 更多的是你希望他们如何使用这个术语而不是他们如何做 - 但如果你告诉我这些印象不合时宜,那么我会接受它......我不会购买每一本新出版的 C++ 书籍,这些天在 comp.lang.c++.moderated 等上闲逛,参加会议等。
    【解决方案4】:

    询问是否添加 const 是 错误的问题,很遗憾。

    将非常量引用与传递非常量指针进行比较

    void modifies(T &param);
    void modifies(T *param);
    

    这个案例主要是关于风格:你想让电话看起来像call(obj)还是call(&obj)?但是,有两点差异很重要。如果您希望能够传递 null,则必须使用指针。如果你重载了操作符,你就不能用指针代替。

    按值比较 const ref

    void doesnt_modify(T const &param);
    void doesnt_modify(T param);
    

    这是一个有趣的案例。经验法则是“廉价复制”类型通过值传递——这些通常是小类型(但不总是)——而其他类型则通过 const ref 传递。但是,如果您无论如何都需要在您的函数中制作副本,请should pass by value。 (是的,这暴露了一些实现细节。C'est le C++。

    将 const 指针与非修改加重载进行比较

    void optional(T const *param=0);
    // vs
    void optional();
    void optional(T const &param); // or optional(T param)
    

    这与上面的非修改情况有关,除了传递参数是可选的。这三种情况之间的区别最小,所以选择让你的生活更轻松的那个。当然,非常量指针的默认值由你决定。

    常量值是一个实现细节

    void f(T);
    void f(T const);
    

    这些声明实际上是完全相同的函数!当按值传递时,const 纯粹是一个实现细节。 Try it out:

    void f(int);
    void f(int const) {/*implements above function, not an overload*/}
    
    typedef void C(int const);
    typedef void NC(int);
    NC *nc = &f;  // nc is a function pointer
    C *c = nc;  // C and NC are identical types
    

    【讨论】:

    • 感谢详细解答!! :)
    • 如果您想传递 NULL 并仍然使用引用,请查看 Null Object Pattern
    • void test(int i) 不是 void test(int const i) 因为后者不允许更改值 i
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-08
    • 2017-11-13
    • 2015-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多