【问题标题】:How to avoid calling a too much parameterized constructor, with virtual inheritance?如何避免使用虚拟继承调用过多的参数化构造函数?
【发布时间】:2015-05-24 17:20:31
【问题描述】:

我想通过以下明显的示例执行虚拟继承:

class A
{
public:
    A(int a) : m_a(a) {}
private:
    int m_a;
};

class B : virtual public A
{
public:
    B(int a, int b) : A(a), m_b(b) {}
private:
    int m_b;
};

class C : virtual public A
{
public:
    C(int a, int c) : A(a), m_c(c) {}
private:
    int m_c;
};

class D : public B, public C
{
public:
    D(int a, int b, int c) : A(a), B(a, b), C(a, c) {}
};

但我不想调用 B(a, b) 和 C(a, c),因为对于这种虚拟继承的特殊情况,参数 a 在 B 和 C 构造函数中是无用的。

我发现以下文章 Jack Reeves 提出了一些替代方案以调用默认构造函数。

http://www.drdobbs.com/cpp/multiple-inheritance-considered-useful/184402074?pgno=2

引用:

如果我们必须在构造 B 和 C 之前完全初始化 A,那么我们必须使用构造函数进行初始化,我们回到我最初展示的内容。或者,也许您可​​以通过以下方式解决:

class B : public virtual A {  // class C is similar
public:
    B(int x) : A(x) {}
protected:
    B() : A(0) {}
};
class D : public B, public C {
public:
    D(int x) : A(x) {}  // B and C are default constructed
};

引用结束。

所以,我保留了受保护构造函数的想法,但我不想使用默认构造函数。我在 A 中实现了一个单参数构造函数(在实践中从未调用过),它使用前向构造函数指向类内“最简单”的有效构造函数(保持 B 和 C 应该在 A 中调用的内容的封装)。这里的单个参数是为了给这个“混蛋”构造函数一个唯一的签名。

// new way
namespace VIRTUAL_INHERITANCE {
struct NEVER_CALLED {};
}

class A
{
public:
    A(int a) : m_a(a) {}
protected:
    A(VIRTUAL_INHERITANCE::NEVER_CALLED vinc) : A(0) {}
private:
    int m_a;
};

class B : virtual public A
{
public:
    B(int a, int b) : A(a), m_b(b) {}
protected:
    B(int b) : A(VIRTUAL_INHERITANCE::NEVER_CALLED()), m_b(b) {}
private:
    int m_b;
};

class C : virtual public A
{
public:
    C(int a, int c) : A(a), m_c(c) {}
protected:
    C(int c) : A(VIRTUAL_INHERITANCE::NEVER_CALLED()), m_c(c) {}
private:
    int m_c;
};

class D : public B, public C
{
public:
    D(int a, int b, int c) : A(a), B(b), C(c) {}
};

我的问题是:

  • 如何避免使用虚拟继承调用过多参数化的构造函数?

  • 是否可以通过其他方式改进“唯一签名”技术(例如使用枚举)?

  • 有没有人有更好的技术来做同样的事情而不必在 A 中定义第二个构造函数?

  • 有什么缺点?

【问题讨论】:

  • 我引用:显而易见的不编译:class D : public B, public C { public: D(int x) : B(x), C(x) {} };这会产生一个错误,抱怨缺少 A 的默认构造函数。要完成这项工作,我们必须编写: D(int x) : A(x), B(x), C(x) {} 虽然这确实有效,这有点愚蠢,因为在为 D 对象调用时,在 B 和 C 构造函数中忽略了对 A 的初始化程序的调用。
  • 你真正想解决什么编程问题?
  • 您阅读了整篇文章吗?我的问题在最后。
  • “但我不想使用默认构造函数。” 为什么?
  • @dyp:好问题!我发现的解决方案确保在真正的构造函数中使用初始化列表,而不是默认构造函数,在我看来,这是一个概念变态。

标签: c++ constructor virtual-inheritance method-signature


【解决方案1】:

我的答案是:

  • 使用默认构造函数,
  • 是的,enum 可以,
  • 没有。如果您希望编译器强制您显式调用A 的构造函数,那么您必须声明附加构造函数。但是,如果您不...使用默认构造函数和 init 函数只是一个常规的参数化构造函数,
  • 具有“唯一签名”构造函数的缺点:
    • A 类中的更多代码,
    • 更多代码在B类中,
    • 更多代码在C类中,
    • 由于虚拟结构导致更多代码。
  • 使用带有“init”函数的默认 c-tor 的缺点:
    • 一定要记得调用init函数参数化构造函数

示例(整洁的)代码:

class A
{
public:
    A(int a) : m_a(a) {}
protected:
    A() = default;
    void initA(int a) { m_a = a; } // optional
private:
    int m_a;
};

class B : virtual public A
{
public:
    B(int a, int b) : A(a), m_b(b) {}
protected:
    B(int b) : m_b(b) {}
private:
    int m_b;
};

class C : virtual public A
{
public:
    C(int a, int c) : A(a), m_c(c) {}
protected:
    C(int c) : m_c(c) {}
private:
    int m_c;
};

class D : public B, public C
{
public:
    D(int a, int b, int c) : A(a), B(b), C(c) {}
    // or optionally:
    D(int a, int b, int c) : B(b), C(c) { initA(a); }
};

【讨论】:

  • 我不太明白你为什么需要initA 函数。你为什么不直接打电话给D(int a, int b, int c) : A(a), B(b), C(c) {}
  • 其实也一样 :) 因为需要显式调用。
  • 不完全。在现实生活场景中,B & C 通常依赖于 a 的正确设置。推迟初始化不是一个好策略...
  • 现实生活中的场景可能非常复杂,我同意。然而,在这种情况下,我们正在讨论专门构造函数的需求。到目前为止,看起来拥有“唯一签名”构造函数(由@cevik 提出)有其好处,因为如果你在初始化D 时忘记调用A 的构造函数,编译器会尖叫你。
猜你喜欢
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 2015-06-21
  • 1970-01-01
  • 2010-11-12
  • 2013-04-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多