来自C#背景,我习惯于使结构不可变的做法.
因此,当我开始使用C++进行编程时,我尝试对通常按值传递的类型执行相同的操作.
我有一个简单的结构,它代表一个自定义索引,只是包装一个整数.因为在C++中const关键字有点类似于readonlyC#,所以实现我的结构似乎是合理的:
struct MyIndex
{
MyIndex(int value) :
Value(value)
{
}
const int Value;
};
Run Code Online (Sandbox Code Playgroud)
这种方法的问题是这个结构的行为与C#中的不可变结构完全不同.不仅MyIndex不能修改变量的Value字段,而且任何现有MyIndex变量(局部变量或字段变量)都不能用另一个MyIndex-instance 替换.
所以下面的代码示例不能编译:
a)使用index作为循环变量.
MyIndex index(0);
while(someCondition)
{
DoWork();
index = MyIndex(index.Value + 1); // This does not compile!
}
Run Code Online (Sandbox Code Playgroud)
b)修改类型的成员字段 MyIndex
class MyClass
{
MyIndex Index;
void SetMyIndex(MyIndex index)
{
Index = index; // This does not compile!
}
};
Run Code Online (Sandbox Code Playgroud)
这两个示例没有编译,构建错误是一样的:
错误C2582:'operator ='函数在'MyIndex'中不可用
这是什么原因?为什么不能将变量替换为另一个实例,尽管它们不是const?构建错误意味着什么是exaclty?那是什么operator =功能?
Mar*_*cze 17
这样做的原因是,在创建变量(局部变量或字段变量)之后的C++中,它不能被另一个实例"替换",只能改变其状态.所以在这一行
index = MyIndex(1);
Run Code Online (Sandbox Code Playgroud)
不是创建新的MyIndex实例(使用其构造函数),而是应该使用其复制赋值运算符更改现有的索引变量.缺少的函数是它缺少的类型的复制赋值运算符,因为如果类型具有const字段,它不会自动生成.operator =MyIndex
它没有产生的原因是它根本无法合理地实现.复制赋值运算符将像这样实现(这在这里不起作用):
MyIndex& operator=(const MyIndex& other)
{
if(this != &other)
{
Value = other.Value; // Can't do this, because Value is const!
}
return *this;
}
Run Code Online (Sandbox Code Playgroud)
因此,如果我们希望我们的结构实际上是不可变的,但行为类似于C#中的不可变结构,我们必须采用另一种方法,即使结构的每个字段都是私有的,并标记我们类的每个函数const.用这种方法
实施MyIndex:
struct MyIndex
{
MyIndex(int value) :
value(value)
{
}
// We have to make a getter to make it accessible.
int Value() const
{
return value;
}
private:
int value;
};
Run Code Online (Sandbox Code Playgroud)
严格来说,MyIndex结构不是不可变的,但它的状态不能用从外部访问的任何东西来改变(除了它自动生成的复制赋值操作符,但这就是我们想要实现的!).
现在上面的代码示例编译正确,我们可以确定我们的类型变量MyIndex不会被改变,除非它们被赋予新值完全替换.