我可以在 C++11 中继承一个构造函数并初始化一些额外的东西而不必重写构造函数吗?

Posted

技术标签:

【中文标题】我可以在 C++11 中继承一个构造函数并初始化一些额外的东西而不必重写构造函数吗?【英文标题】:Can I inherit a constructor in C++11 and initialise something extra without having to rewrite the constructor? 【发布时间】:2018-07-06 22:59:37 【问题描述】:

我很高兴地发现在 C++11 中我们可以继承如下构造函数:

class Foo
public:
    Foo(int a, double b, std::string name, char somethingElse);
;

class Derived : public Foo
public:
    using Foo::Foo;
;

但是我发现我经常扩展基类,其中可能有一个或两个额外的功能,我需要通过可能作为额外参数或其他东西传递来初始化几个额外的成员。在这种情况下,我似乎必须重写构造函数并将所有参数传递给基数。我想知道是否有更好的解决方案。我想也许只是使用继承的构造函数,然后在构造后在下一行初始化额外的成员,但这似乎不对:

Derived d = Derived(6, 6.0, "name", 'a');
d.extraMember = 4;
d.funcptr = &somefunction;

我认为这是一个很棒的功能,但后来我越来越意识到我的扩展类需要额外的初始化信息。

这是我的代码中的示例:

struct Window

    Window(Window* parent, vec2 position, vec2 size, const String& name);
;
struct ConsoleWindow : Window

    using Window::Window;
    // But I've had to rewrite the constructor again because in this constructor I do stuff like 
    //GUI::bIsConsoleWindowActive = true;
    //GUI::unselectWindow();
    //GUI::selectedWindow = this;


在我看来,如果不重写构造函数并调用基并传递所有值,就无法在构造过程中添加额外的东西。这在我的课堂上很常见。

【问题讨论】:

如何在不提供接受六个变量的构造函数的情况下初始化一个需要六个变量参数的对象?你能提供一个你希望的代码示例吗? @Galik 我从我的代码中添加了一个示例,以防更容易理解我的问题所在。 那么你希望能够而不是重写构造函数做什么? @Galik 好吧,我不知道该语言的能力,乍一看,我的选项似乎重写了构造函数,我似乎经常对每个新类或使用一对在构造对象后初始化额外成员的额外行,或者我不知道,我想到了一个 initialise() 函数。讲故事的人的方法不错。 【参考方案1】:

如果您的基类是可复制/可移动的并且不是抽象的,您可以将构建基类的负担推到派生对象的声明上。只需接受一个基础对象:

Derived(Base b, int extraMember) :
  Base(std::move(b)), extraMember(extraMember)


声明变成这个的地方

Derived dBase6, 6.0, "name", 'a', 4;

这也不完美,因为它对您的基类提出了某些可能不成立的要求。如果它看起来像聚合初始化,那是因为它开始模仿它。

恐怕唯一能完全适应任何情况的方法就是像你现在一样拼出一个新的 c'tor。

【讨论】:

这实际上不是一个坏方法。我猜想这种继承自 C++11 以来的构造函数只有在派生类仅覆盖函数时才有用,因为我经常发现自己重写它们。哦,好吧。 @Zebrafish - 不仅仅是覆盖函数。有时我们添加的成员数据只是默认初始化的,因此任何 c'tor 仅用于为基类提供参数。在这种情况下,继承也有意义。 我想新的类内初始化器也有帮助。 @Zebrafish - 他们确实如此。总的来说,我想说在 C++11 中定义一个类的初始化方式比以前更容易。然而,它仍然不完美。【参考方案2】:

根据您的问题,我不确定您是否意识到可以从派生构造函数调用基本构造函数。使用您的示例:

struct Window

   Window(Window* parent, vec2 position, vec2 size, const String& name);
;
struct ConsoleWindow : Window

   ConsoleWindow(Window* parent, vec2 position, vec2 size, const String& name, int otherValue)
   : Window(parent, position, size, name)
   
      // do something with `otherValue` here. 
   
;

请参见此处的示例:calling the base class constructor in the derived class constructor

【讨论】:

是的,我就是这样做的。通常我的每个新类在构造函数中都有不同的东西,我不能真正使用新的 c++11 构造函数继承,因为它必须是相同的 @Zebrafish 我想我只是不明白困难在哪里。您不喜欢枚举基本构造函数的所有参数吗?不能做那么多工作,可以吗?复制粘贴?只是不要将接口更改为您的基类! :)【参考方案3】:

您的问题不清楚您是否能够更改基类。假设你是,你可以考虑将你的基本构造函数参数打包成一个struct,它可以从派生的构造函数向上转发。

这在实现 builder design pattern 时经常出现,这是解决您所描述的伸缩构造函数反模式的一种方法。

struct FooParts 
    int i;
    double d;
    string s;
    char c;
;

class Foo 
public:
    Foo(FooParts const & parts) : parts(parts) 
private:
    FooParts parts;
;

class Derived : public Foo 
public:
    Derived(FooParts const & parts, bool extra) : Base(parts), member(extra) 
private:
    bool member;
;

【讨论】:

【参考方案4】:

执行您描述的最直接的方法是在派生类的初始化列表中使用基类构造函数:

class Foo
public:
    Foo(int a, double b, std::string name, char somethingElse);
;

class Derived : public Foo

public:
    Derived::Derived(int a, double b, std::string name, char somethingElse, 
                     int _extraMember, char derivedSpecificThing) :
        // Initialization list starts here
        Foo(a, b, name, somethingElse),
        extraMember(_extraMember)
    
        // Do something with derivedSpecificThing etc.
    

private:
    const int extraMember;
;

using Foo::Foo 仅将基类的构造函数公开为派生类的附加构造函数。当然,基类的构造函数不能初始化它不知道的派生类的附加成员。

【讨论】:

以上是关于我可以在 C++11 中继承一个构造函数并初始化一些额外的东西而不必重写构造函数吗?的主要内容,如果未能解决你的问题,请参考以下文章

在 C++0x 中继承构造函数

C++中继承类的构造函数

ES5和ES6中继承的不同之处

JavaScript中继承的实现方式

JavaScript中继承的实现方式

javascript中6中继承方式