如何在 C++03 中伪造构造函数继承?

Posted

技术标签:

【中文标题】如何在 C++03 中伪造构造函数继承?【英文标题】:How can I fake constructor inheritance in C++03? 【发布时间】:2011-10-29 10:06:05 【问题描述】:

据我所知,您不能在 C++ 中继承构造函数。但是在某些情况下,您可能需要看起来像实例化继承类的方式与实例化它们的基类一样:

struct Base 
  int i;
  int const j;
  Base(int i, int j) : i(i), j(j) 
;

// option 1)
struct Derived1 : Base 
  Derived1(int i, int j) : Base(i,j) 
;
Base* baseFactory() 
  return new Derived1(42,47);


// option 2)
struct Derived2 : Base 
;
Base* baseFactory() 
  Base* b = new Derived2();
  b->i = 42;
  b->j = 47; // broken!
  // alternative could be to implement Base::operator=
  return b;

请注意,如果不是为它们的基类,派生类可能是默认构造的。

我认为选项 1 是通常所做的,但您输入的代码没有表达任何新内容。选项 2 破坏了 const 的正确性(并阻止您将引用用作成员),因为所有内容都必须是可分配的。

有什么好的办法吗?

编辑:C++11 很棒,但不幸的是我需要 C++03 解决方案。

【问题讨论】:

如果可以,用c++11,直接解决你的问题。 我不明白你发现你的第一个选项有什么问题......这是在构造过程中初始化基础对象的标准方法。如果您稍后需要完成更多初始化,您可以在派生调用 C'tor 主体中相应地更新字段。 选项2根本不编译,怎么可能是选项? @UncleBens:如果你没有const& 成员,我会工作。 【参考方案1】:

选项 1 是 C++03 中的惯用方式,它确实需要更多的输入,但语言就是这样。选项 2 甚至不接近等效项,您将无法初始化 Derived2,因为 Base 没有默认构造函数,并且 隐式声明 Derived2 的默认构造函数的定义需要它。

但是除了语言的技术难度之外,您正在用 construction 换取 two-phase construction 这绝不是一个好主意,同时迫使您使用Derived2 动态分配的对象。

【讨论】:

【参考方案2】:

还有一种替代方法:

// option 3)
struct Derived3 : Base 
  Derived3(const Base &b) : Base(b) 
;
Base* baseFactory3() 
    return new Derived3(Base(42,47));

如果构建一个完整的 Base 对象很昂贵或需要外部资源,这可能不是一个好主意。在这种情况下,您可以创建一个轻量级对象,该对象带有 Base 的构造函数参数,这样您就有多个 BaseArguments 构造函数和一个接收 BaseArguments 的 Base 构造函数,而不是多个 Base 构造函数。但是,我认为在大多数情况下不会有很多人会考虑这种好的风格。

【讨论】:

-1 这是一个糟糕的解决方案。撇开应该使用依赖注入不谈,这只是令人费解,很难理解发生了什么。 @Paul Manta:我非常倾向于接受这个答案,因为它是最干净的解决方案。但是,您似乎有强烈的批评,所以也许您愿意自己详细说明或发布更好的答案? @bitmask:Paul 似乎只是在说这令人困惑。此外,如果可以为 Base 提供 C++03 移动构造函数 Base(Base& rhs, int moveconstructor),那么您不必担心 Base 的重量,这会增加速度(有时)和混乱。 @Mooing 关于 C++03 移动构造函数,Alexandrescu 的这篇文章很有趣:drdobbs.com/cpp/184403855【参考方案3】:

C++11 支持继承的构造函数。

http://en.wikipedia.org/wiki/C%2B%2B11

然而,并非所有现在支持 C++11 子集的编译器都支持所有功能。

Microsoft Visual C++ 2010 支持几个新的 C++0x 功能,但不支持继承的构造函数,这里是一个列表:http://blogs.msdn.com/b/vcblog/archive/2010/04/06/c-0x-core-language-features-in-vc10-the-table.aspx

GCC 支持更多新功能,但不支持继承的构造函数。见http://gcc.gnu.org/projects/cxx0x.html

要使 GCC 能够使用 C++0x 功能进行编译,您需要添加 -std=c++0x-std=gnu++0x 编译参数。 Visual C++ 2010 默认启用这些。

你只需要等待:)

对于以前版本的标准没有干净的解决方案,因此它是在 C++11 中引入的,因为它不可能与以前的版本一起使用。 所以,你唯一能做的就是复制你的构造函数并调用基构造函数。

【讨论】:

关于等待的话题:我就像星际迷航中的星际飞船船长:我想要一切,我现在就想要(在所有平台上):)【参考方案4】:

这里是 Builder Pattern 的一个变体,我一直认为它可能很有用,但我从未真正有机会使用它;我想您可以将其称为构造函数模式。 :P

它还提供了一些其他优点,例如不必按特定顺序指定参数:您只需指定您感兴趣的内容以及您想要的任何顺序。

我仍然认为只有当你的类有很多具有不同参数的构造函数时这是合理的。否则,这只是一个烦恼。

class Person

  public:
    class Constructor;
;

class Person::Constructor

  public:
    Constructor& name(const std::string&);
    Constructor& age(int);
    Person* make();
;

Person* pers = Person::Constructor()
    .name("Bob Marley").make();


class Employee : public Person

  public:
    class Constructor;
;

class Employee::Constructor : public Person::Constructor

  public:
    Constructor& salary(double);
    Employee* make();
;

Employee* emp = Employee::Constructor()
    .name("Emilly Smith").age(23).make();

但同样,只有当你的类有很多带有很多参数的构造函数并且你想避免为它们编写多个重载时,这才是合理的;否则,这只会​​增加太多复杂性而没有任何实际好处。

我提到我不喜欢 han 提出的解决方案。这是因为(1)您将需要(重新)声明子类中的许多构造函数,以需要在工厂类中放置相同数量的函数; (2)它是hacky并且意图不明确; (3) 你可以认为这违反了依赖注入。

【讨论】:

当我过去看到这个时,构造函数是Person 构造函数的参数,它允许本地分配。我也没有立即看到这如何解决 OP 的问题。 @Mooing:我认为这个想法是父构造函数参数由子构造函数继承,通过Employee::ConstructorPerson::Constructor继承。 如果问题是多个(或复杂)构造函数,这很方便,但如果只是将少量构造函数复制到派生类,则不是。 如果您将Person::Constructor 设为Person 的基类,您可以节省大量的成员名称输入。现在,将 Constructor::make() 方法更改为 Person Person(Constructor&) 的构造函数,以便更轻松地构造对象。但是,嘿,这基本上就是 han 所提议的。 @bitmask 您可以节省名称输入,但这样做没有意义。

以上是关于如何在 C++03 中伪造构造函数继承?的主要内容,如果未能解决你的问题,请参考以下文章

在构造函数继承中使用默认构造函数

C++--继承中的构造与析构父子间的冲突

构造函数继承问题c

复制构造函数不被继承

C++ 继承:子类实例同时使用子构造函数和父构造函数

C++/MFC多重继承调用基类构造函数