我有一个这样的类层次结构:
class Base {
public:
virtual bool foo() const = 0;
static Base& getHeadInstance();
};
class Concrete: public Base {
public:
explicit Concrete(Base& f): fwd(f) {}
// ... other member functions elided
bool foo() const override { return /* some calculation */ && fwd.foo(); }
private:
Base& fwd;
};
Run Code Online (Sandbox Code Playgroud)
这样我就可以构造一系列这样的实例:
Concrete c1(Base::getHeadInstance());
Concrete c2(c1);
Concrete c3(c2);
Run Code Online (Sandbox Code Playgroud)
这样就c3可以做出决定,并可能要遵循c2,而后者又可以c1像Chain of Responsibility模式一样遵循。
问题在于c2且c3构造不正确,其fwd成员始终引用Base::getHeadInstance()。
这里出了什么问题?解决方法是什么?
更新:
静态成员返回什么都无所谓。假设它返回了一个实例:
class Head: public Base {
public:
Head() = default;
private:
bool foo() const override { return true; }
};
Base& Base::getHeadInstance(){ static Head head; return head; }
Run Code Online (Sandbox Code Playgroud)
我以前也遇到过同样的问题。归结为您的自定义构造函数Derived(Base&)与隐式定义的副本构造函数的重载解析Derived(const Derived&);,其中隐式副本构造函数只是赢了。删除它并不能解决问题,它仍然参与了重载解决方案(但是它确实阻止了错误的事情无声地发生)。
这是一个简化的示例:
struct Base
{
virtual ~Base();
};
struct Derived : Base
{
Derived(Base&);
Derived(const Derived&); // Implicitly or explicitly declared in any case.
};
Derived getDerived();
void test()
{
Derived d1 = getDerived();
Derived d2(d1); // copies
}
Run Code Online (Sandbox Code Playgroud)
有几种方法可以使代码按照编写方式的方式进行操作(请参阅其他答案),但是我想指出,您应该格外小心,以免代码的下一个读者你有困惑。强制转换为Base&,重用了副本构造函数的语义或类似的用法,Derived(Base*);都会在未来的读者中提出疑问。您可以尝试通过文档解决此问题,但是可能有人会错过它并感到困惑。我建议使意图尽可能明确和可见,例如:
enum class ConstructFromBase { Tag };
struct Derived : Base
{
Derived(Base&, ConstructFromBase);
Derived(const Derived&) = delete;
};
Derived getDerived();
void test()
{
Derived d1 = getDerived();
Derived d2(d1, ConstructFromBase::Tag);
}
Run Code Online (Sandbox Code Playgroud)
这应该非常清楚地传达意图,并且几乎不花任何费用。(还有许多其他方式可以编写和命名此类标签,我的标签可能不是最规范的...)
| 归档时间: |
|
| 查看次数: |
105 次 |
| 最近记录: |