使用C++基类构造函数?

82

在使用模板时,我遇到了一个需要让继承类可以访问基类构造函数来创建对象,以减少复制和粘贴操作的需求。我曾考虑通过using关键字以与函数案例相同的方式解决这个问题,但它并没有起作用。

class A
{
public: 
    A(int val) {}
};

class B : public A
{
};

class C : public A
{
public:
    C(const string &val) {}
};

class D : public A
{
public:
    D(const string &val) {}
    using A::A;              // g++ error: A::A names constructor
};

void main()
{
    B b(10);                // Ok.   (A::A constructor is not overlapped)
    C c(10);                // error: no matching function to call to 'C::C(int)'
}

我的问题是:是否有一种方法可以在已声明继承类的新构造函数之后导入基类构造函数?

还是唯一的选择是声明新的构造函数并从初始化列表中调用基类构造函数?


1
是的,你说得对。现在已经修复了。 - minyor
可能是 如何在 C++ 中使用基类的构造函数和赋值运算符? 的重复。 - Ciro Santilli OurBigBook.com
6个回答

118

是的,从C++11开始:

struct B2 {
    B2(int = 13, int = 42);
};
struct D2 : B2 {
    using B2::B2;
// The set of inherited constructors is
// 1. B2(const B2&)
// 2. B2(B2&&)
// 3. B2(int = 13, int = 42)
// 4. B2(int = 13)
// 5. B2()

// D2 has the following constructors:
// 1. D2()
// 2. D2(const D2&)
// 3. D2(D2&&)
// 4. D2(int, int) <- inherited
// 5. D2(int) <- inherited
};

欲了解更多信息,请参阅http://en.cppreference.com/w/cpp/language/using_declaration


这对我不起作用。我得到了一个“没有构造函数的实例与参数列表匹配”的错误。 - Charles
2
@charlesrockbass 这意味着你的 B2 没有与你的参数列表匹配的构造函数,或者该构造函数不可见。 - Sergei Krivonos
如果您必须指定命名空间,那么这个语法会是什么样子呢? 例如,对于以下类:struct my_container : std::vector<int>{} - Kartoffel
@Kartoffel,1)using base=std::vector<int>; 2)using base::base; - Sergei Krivonos

45

更喜欢初始化:

class C : public A
{
public:
    C(const string &val) : A(anInt) {}
};

在C++11中,您可以使用继承构造函数(其语法与您的示例D中所看到的相同)。

更新:自版本4.8以来,GCC已可用继承构造函数。


如果您不认为初始化很有吸引力(例如由于实际情况中可能存在的可能性数量),则对于某些TMP结构,您可能会偏爱这种方法:

class A
{
public: 
    A() {}
    virtual ~A() {}
    void init(int) { std::cout << "A\n"; }
};

class B : public A
{
public:
    B() : A() {}
    void init(int) { std::cout << "B\n"; }
};

class C : public A
{
public:
    C() : A() {}
    void init(int) { std::cout << "C\n"; }
};

class D : public A
{
public:
    D() : A() {}
    using A::init;
    void init(const std::string& s) { std::cout << "D -> " << s << "\n"; }
};

int main()
{
    B b; b.init(10);
    C c; c.init(10);
    D d; d.init(10); d.init("a");

    return 0;
}

1
添加注释,因为SO一直建议这样做,我更喜欢使用“using”语法,因为在通常情况下它要短得多,对我来说看起来更加简洁。 - Lukas Salich

14

不是这样做的。初始化基类的正常方式是在初始化列表中完成:

class A
{
public: 
    A(int val) {}
};

class B : public A
{
public:
  B( int v) : A( v )
  {
  }
};


void main()
{
    B b(10);
}

4

您需要在每个派生类中声明构造函数,然后从初始化列表中调用基类构造函数:

class D : public A
{
public:
    D(const string &val) : A(0) {}
    D( int val ) : A( val ) {}
};

D variable1( "Hello" );
D variable2( 10 );

C++11允许您在声明D时使用using A::A的语法,但目前并非所有编译器都支持C++11功能,因此最好使用旧的C++方法,直到这个特性在您的代码将要使用的所有编译器中得以实现。


2
你确定它能编译通过吗?A在第一个构造函数中是如何初始化的? - BЈовић
啊,是的,我有点匆忙地处理了这个例子。现在已经修复了。 - obmarg

3
class A
{
public: 
    A(int val) {}
    A(string name) {}
};

class B : public A
{
using A::A;
};

除了其他答案之外,如果基类有多个构造函数,而你只想继承其中的一些,那么可以删除不需要的构造函数。

 class B : public A
 {
    using A::A;
    B(string) = delete;
 };

1

这里有一个关于超类构造函数调用规则的好讨论。你总是希望在派生类构造函数之前调用基类构造函数,以便正确地形成一个对象。这就是为什么使用这种形式的原因。

  B( int v) : A( v )
  {
  }

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