C const-reference语义?

考虑下面的示例应用程序.它展示了我称之为有缺陷的类设计.
#include <iostream>

using namespace std;

struct B
{
 B() : m_value(1) {}

 long m_value;
};

struct A
{
 const B& GetB() const { return m_B; }

 void Foo(const B &b)
 {
  // assert(this != &b);
  m_B.m_value += b.m_value;
  m_B.m_value += b.m_value;
 }

protected:
 B m_B;
};

int main(int argc,char* argv[])
{
 A a;

 cout << "Original value: " << a.GetB().m_value << endl;

 cout << "Expected value: 3" << endl;
 a.Foo(a.GetB());

 cout << "Actual value: " << a.GetB().m_value << endl;

 return 0;
}

输出
原值:1
预期价值:3
实际值:4

显然,程序员被b的常量所欺骗.错误的b指向此,这会产生不良行为.

我的问题:在设计getter / setter时你应该遵循什么const规则?

我的建议:如果可以通过成员函数通过引用设置,则永远不要返回对成员变量的引用.因此,要么按值返回,要么按值传递参数. (现代编译器无论如何都会优化掉额外的副本.)

解决方法

ObvIoUsly,the programmer is fooled by the constness of b

正如有人曾经说过的那样,你继续使用那个词.我认为这并不意味着你的意思.

Const意味着您无法更改该值.这并不意味着价值不能改变.

如果程序员被一些其他代码可以改变他们无法改变的事实所迷惑,那么他们需要更好的基于别名的基础.

如果程序员被令牌“const”听起来有点像“常量”但意味着“只读”这一事实所迷惑,那么他们需要在他们正在使用的编程语言的语义上有更好的基础.

因此,如果您有一个返回const引用的getter,那么它是您无权更改的对象的别名.这没有说明它的价值是否是不可改变的.

最终,这归结为缺乏封装,而不是应用得墨忒耳定律.通常,不要改变其他对象的状态.向他们发送消息,要求他们执行操作,这可能(取决于他们自己的实现细节)改变他们的状态.

如果你将B.m_value设为私有,那么你就不能写出你拥有的Foo.你要么让Foo成:

void Foo(const B &b)
{
    m_B.increment_by(b);
    m_B.increment_by(b);
}

void B::increment_by (const B& b)
{
    // assert ( this != &b ) if you like 
    m_value += b.m_value;
}

或者,如果您想确保该值是常量,请使用临时值

void Foo(B b)
{
    m_B.increment_by(b);
    m_B.increment_by(b);
}

现在,增加一个值本身可能是合理的,也可能是不合理的,并且很容易在B :: increment_by中进行测试.您还可以测试A :: Foo中的& m_b ==& b,但是一旦您有几个级别的对象和对象引用其他对象而不是值(因此& a1.bc ==& a2.bc并不意味着& a1.b ==& a2.b或& a1 ==& a2),那么你真的必须要知道任何操作都有可能是别名.

别名意味着通过表达式两次递增与第一次计算表达式时递增表达式的值不同;没有真正的方法,在大多数系统中,复制数据的成本不值得避免别名的风险.

传递结构最少的参数也很有效.如果Foo()花了一个长而不是一个它必须得到长的对象,那么它就不会遭受别名,你不需要写一个不同的Foo()来增加m_b的值.

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...