我遇到了设计问题。让我们先说这段代码
struct Trial{
const double a;
Trial(double a_) : a(a_){}
};
int main(){
Trial t1(1.);
Trial t2(2.);
t1 = t2;
}
无法编译,因为编译器默认不构建Trial::operator=
,因为Trial::a
是const
。这非常明显。
现在重点是,首先是代码
struct Trial{
const double a;
Trial(double a_) : a(a_){}
};
struct MyClass : private Trial{
MyClass(double a_) : Trial(a_), i(0) {};
void wannaBeStrongGuaranteeMemberFunction(){
MyClass old(i);
bool failed = true;
//... try to perform operations here
if(failed)
*this = old;
}
unsigned int i;
};
int main(){
MyClass m1(1.);
m1.wannaBeStrongGuaranteeMemberFunction();
}
我需要为类的某些方法提供强大的异常安全性,这些方法派生自Trial
。这些方法对无穷无尽的一系列成员(示例中为i
)执行无休止的一系列操作,这使得“手动”恢复操作变得不切实际。因此,我决定最好执行整个类的副本,如果有任何失败则将其复制回来。
小括号,代码只是一个例子。遗留的现实世界代码中的一切都要复杂得多。
在这个例子中,只复制i
就可以了,但实际代码并非如此。
此外,操作具有多个(和复杂的)执行路径,因此将它们“读取”为“事务”将是一种痛苦。
此外,我正在使用范围保护来实现这一点,因此在实际代码中正确地管理异常。
当然整个事情都没有编译,因为行*this = old
。
您如何解决问题/解决问题?
答案 0 :(得分:2)
显而易见的答案是修改Trial
以便它支持
任务也是如此。除非这一点,如果你唯一的原因
想支持任务是提供强有力的保证,
你可以实现自己的赋值运算符
private,忽略了基类;既然你知道那个
两个基类是相同的,没有必要分配
他们之间。
请注意,强保证通常涉及交换
分配。哪个不会改变问题:你不能交换
两个版本的Trial
也是。你很有可能
类似的东西:
class MyClass : private Trial
{
class Protected
{
bool myCommitted;
MyClass* myOwner;
MyClass myInstance;
public:
MyClass( MyClass& owner )
: myCommitted( false )
, myOwner( &owner )
, myInstance( owner )
{
}
~MyClass()
{
if ( myCommitted ) {
myOwner->swap( myInstance );
}
}
MyClass& instance() { return myInstance; }
void commit() { myCommitted = true; }
};
public:
void protectedFunc()
{
Protected p( *this );
p.instance().unprotecedVersionOfFunc();
// ...
p.commit();
}
任何异常都会使对象保持不变。 (你可以,
当然,颠倒逻辑,在this
上进行修改,
如果没有提交则交换。)做事的好处
这样你就可以“撤消”内存中的任何变化
分配等。
最后:你真的想在Trial
中使用const成员吗?
实现这一目标的通常方法是制作a
非const但是私有,只提供一个getter。这意味着
Trial::a
实际上是const,除之外是完整的
分配