C++11虚拟复制构造函数

6

我正在阅读Mark Joshi的《C++设计模式和衍生品定价》,并在C++11中实现他的代码。一切进行得非常顺利,直到我遇到第四章讨论虚拟复制构造函数。

PayOffDoubleDigital thePayOff(Low, Up);
VanillaOption theOption(thePayOff, Expiry);

这里的问题在于,VanillaOption 包含对 thePayOff 的引用。如果是这种情况,当有人修改 thePayOff 时,可能会不知情地修改 theOption 的行为。他建议的解决方案是在 PayOffDoubleDigital 的基类 PayOff 中创建虚拟复制构造函数,以便 theOption 包含自己的副本:
virtual PayOff* clone() const = 0;

然后在每个继承类中定义:

PayOff* PayOffCall::clone() const
{
    return new PayOffCall(*this);
}

在C++11中,直接返回new似乎是不合适的做法。那么使用C++11,应该如何处理呢?


3
零规则:创建一个拥有适当所有权语义的句柄类,并在所有地方使用它。 - R. Martinho Fernandes
看起来我需要进行一些额外的研究。谢谢Martinho。 - BDig
我之前写过这个:http://flamingdangerzone.com/cxx11/2012/08/15/rule-of-zero.html - R. Martinho Fernandes
2个回答

13
他提出的解决方案是在PayOffDoubleDigital的基类中创建一个虚拟复制构造函数[...] 首先,clone()不是一个复制构造函数。对于类X的复制构造函数是一种没有返回类型的特殊成员函数,通常具有以下签名:
X(X const&)

而且 可能 有签名:

X(X&)

函数clone()是一个常规(虚拟)函数,它的特殊含义由用户-即您-识别为创建对象的克隆体,但编译器并不知道clone()的用途。

在C++11中,使用new可能不合适

这是真的,在C++11中使用new是不符合惯例的。实际上,在C++11中,除非您正在进行非常低级别的内存管理(除非您确实需要),否则几乎永远不应该使用new-而在C++14中,可以删掉"几乎"这个词。不幸的是,在这种情况下,可能需要使用new

我之所以这么说,是因为我认为在这里返回一个unique_ptr听起来是合适的(选项对象必须持有自己的PayOff对象,并且只要选项对象存在,PayOff对象就必须保持活动状态),而在C++11中没有std::make_unique()函数(它将在C++14中提供):

std::unique_ptr<PayOff> PayOffCall::clone() const
{
    return std::unique_ptr<PayOff>(new PayOffCall(*this));
}

VanillaOption(或其基类)持有一个unique_ptr而不是原始指针,将使删除clone()返回的PayOff对象变得不必要。反过来,不需要删除该对象意味着无需定义用户提供的析构函数,并且无需关注 Rule of Three Rule of Five 或其他内容。

尽可能遵循R. Martinho's Fernandes's advice并采用 Rule of Zero


感谢您的精心回复。这是我难以掌握的C++领域。如果可以的话,我会给它打分的。但是,由于这是我的第一个问题,我没有必要的声望。需要更多的人来评价这个问题 :) - BDig
@BDig:不用担心,继续提出好问题,你很快就会获得声望 :) 很高兴我能帮到你,祝你的书好运。 - Andy Prowl

1

在处理所有权时,最清晰的解决方案通常是返回智能指针:它既保证了异常安全性(没有内存泄漏的风险),又清楚地表明了对象的所有者。

使用unique_ptr还是shared_ptr取决于您自己。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接