给定一个不可变的C ++对象(其成员为const
,这意味着它的operator=()
方法不存在),如何实现这个简单的过程模式(需要Object::operator=()
来实现存在):
Object someObject {17};
// ...
if (...) {
someObject = Object {42};
}
// ...
use(someObject);
答案 0 :(得分:2)
解决方法是使用shared_ptr。
shared_ptr<Object> someObject(new Object(17));
shared_ptr<Object> anotherObject(new Object(42));
// ...
if (...) {
someObject = anotherObject;
}
use(someObject);
答案 1 :(得分:1)
在这种情况下我的模式是将初始化代码提取到函数中:
Object ini(...){
if(...) {
return Object{42};
}
return Object{17};
}
.....
Object someObject=ini(...);// copy constructor used (or not, because of RVO)
use(someObject);
如果初始化很简单,您可以使用:
Object someObject = ...? Object{42} : Object{17};
声明o
- 变量const。
如果someObject=17
被使用,然后被someObject=42
取代 - 它只会破坏通过声明某些成员const所追求的良好意图。
有两种选择:
const
并不是一个好主意 - 它可以撤消,并且可以添加一个分配运算符。Object
,因为它意味着要使用。不应该轻易做什么:使用指针/引用制作一些技巧 - 它只会让你的代码变得更加复杂。如果需要,最好使用新变量:
Object someObject {17};
// ...
Object newSomeObject = ... ? Object {42} : someObject {17};
use(newSomeObject);
如果复制旧对象可能是性能问题,则可以通过这种方式重构代码,
use(need42(..) ? create42() : object17);
可以在不复制数据的情况下使用。此解决方案假定use
使用其参数的const引用,或者参数按值传递。
在我看来,不可变对象的每次更改都应该产生一个新对象,否则会发生以下情况:
ImmutableObject obj(1);
ImmutableObject &ref=obj;//ref.member=1
...
obj=ImmutableObject(2);
//ref.member!=1, that is puzzling, I assumed ref to be immutable!
现在,您的对象的用户(通过ref
)会因为对象被更改而烦恼!不变性的全部意义在于,您可以推断,永远不会更改的值。如果他们可以改变,那么使用&#34; immutables&#34;并没有太多的优势。首先。
答案 2 :(得分:0)
当您的初始化逻辑可能很简单时,其他答案会起作用,但如果您正在解开一些意大利面条代码,那么可能会帮助。
如果不是,请从java(他们可能没有发明它,但我看到java程序员最常使用它) - 构建器模式。以下是在C++
中实现它的两种可能方式。
#include <string>
class ImmutableClass {
public:
ImmutableClass(int a, int b, std::string c) : a_(a), b_(b), c_(c) {}
// Getters...
private:
ImmutableClass& operator=(const ImmutableClass&) = delete;
const int& GetA() {return a_;}
const int& GetB() {return b_;}
const std::string& GetC() {return c_;}
const int a_;
const int b_;
const std::string c_;
};
struct ImmutableClassBuilderExampleOne {
public:
// Note the default initialization to avoid undefined behavior.
int a = 0;
int b = 0;
std::string c;
};
// Less boilerplate, less encapsulation, if that's your thing.
ImmutableClass BuildImmutableClass(const ImmutableClassBuilderExampleOne& icb) {
return ImmutableClass(icb.a, icb.b, icb.c);
}
// More boilerplate, more encapsulation, can be "tidied" with macros.
class ImmutableClassBuilderExampleTwo {
public:
const ImmutableClass build() {
return ImmutableClass(a_, b_, c_);
}
ImmutableClassBuilderExampleTwo& setA(const int a) {
a_ = a;
return *this;
}
ImmutableClassBuilderExampleTwo& setB(const int b) {
b_ = b;
return *this;
}
ImmutableClassBuilderExampleTwo& setC(const std::string& c) {
c_ = c;
return *this;
}
private:
// Note the default initialization to avoid undefined behavior.
int a_ = 0;
int b_ = 0;
std::string c_;
};