C/C++编程:继承构造函数

引入

在传统C++中,如果派生类要使用使用基类的构造函数,通常需要在构造函数中显式声明:

struct A{
public:
    A(int i) {}
};

struct B : A{
    B (int i) : A(i){}
};

B派生自A,B又在构造函数中调用A的构造函数,从而完成构造函数的传递。但是如果基类中有大量的构造函数时,可能需要这样写:

struct A{
 	A(int i){};
 	A(double d, int i){}
 	A(float f, int i, const char*c){}
};

struct B: A{
	B(int i): A(i) {}
	B(double d, int i) : A(d, i) {};
	B(float f, int i, const char*c) : A(f, i, c){}
	
	virtual void ExtraInterface{};
};

上面B继承自A,但是只添加了一个接口ExtraInterface。如果我们想要在构造B时拥有A这样多的构造方法的时候,就必须意义“透传”各个接口,这时候就很不方便了。

我们知道, C++中规定,如果派生类中要使用基类成员函数,可以用using声明来完成,比如:

#include
using namespace std;

struct Base{
	void f(double f) {cout <<"Base:" << endl;}
};

struct Derived : Base{
	using Base::f;
	void f(int i) {cout <<"Derived :" << endl;};
};

int main(){
	Base b;
	b.f(4.5);

    Derived  d;
    d.f(4.5);
}

上面用了using声明,声明派生类Derived 也使用基类版本的f,派生类中实际上有两个f版本的函数。
在这里插入图片描述

同样的,这个想法被扩展到了构造函数上,子类可以通过使用using声明来继承基类的函数:

struct A{
 	A(int i){};
 	A(double d, int i){}
 	A(float f, int i, const char*c){}
};

struct B : A{
	using A::A; //继承构造函数
	virtual void ExtraInterface{};
};

实际上,C++11标准继承构造函数被设计为跟派生类中的各种类默认构造函数(默认构造、析构、拷贝构造)一样,是隐式声明的。这意味着如果一个继承构造函数不被相关代码实用,编译器不会为其产生真正的函数代码。

有时候,基类构造函数的参数有默认值。对于继承构造函数来讲,参数的默认值是不会被继承的。事实上,默认值会导致基类产生多个构造函数的版本,这些版本都会被派生类继承。看个例子:

struct A{
    A(int = 3, double = 2.4){}
};

struct B :A{
    using A::A;
};

上面,B可能从A中继承来的候选构造函数有如下一些:

  • A(int = 3, double = 2.4)
  • A(int = 3)
  • A(const A&); // 默认复制构造函数
  • A(); // 默认构造函数

相应的,B中的构造函数将会包括如下一些。

  • B(int, double); // 继承自A
  • B(int); //继承自A
  • B(const B&); // 不是继承的默认复制构造函数
  • B(); // 不是继承的默认构造函数

另外:

  • 如果基类中的构造函数被声明为私有成员函数,或者派生类是从基类中虚继承的,那么就不能在派生类中声明继承构造函数了。
  • 如果一旦使用了继承构造函数,编译器器就不会在为派生类生成默认构造函数了,也就是说类似下面,继承构造函数没有包含一个无参数版本,就不能通过编译:
#include
using namespace std;
struct A{
    A(int){}
};

struct B : A{
    using A::A;
};


int main(){
    B b;  // error:B没有默认构造函数
}

总结

为此C++11利用关键字using引入了继承构造函数的概念(当派生类要使用基类的成员函数时用)。

#include 

class Base{
public:
    int val1;
    int val2;

    Base(){
        val1 = 1;
    };

    Base(int val) : Base(){ // 委托Base()构造函数
        val2 = val;
    }
};

class Subclass : public  Base{
public:
    using  Base::Base; // 继承构造
};

int main(){
    Subclass subclass(3);
    printf("%d, %d", subclass.val1, subclass.val2);
    return 0;
}

也就是说,派生类可以通过使用using声明从直接基类继承构造函数:

#include 
using namespace std;

class Base
{
public:
    Base() { cout << "Base()" << endl; }
    Base(const Base& other) { cout << "Base(Base&)" << endl; }
    explicit Base(int i) : num(i) { cout << "Base(int)" << endl; }
    explicit Base(char c) : letter(c) { cout << "Base(char)" << endl; }

private:
    int num;
    char letter;
};

class Derived : Base
{
public:
    // Inherit all constructors from Base
    using Base::Base;

private:
    // Can't initialize newMember from Base constructors.
    int newMember{ 0 };
};

int main()
{
    cout << "Derived d1(5) calls: ";
    Derived d1(5);
    cout << "Derived d1('c') calls: ";
    Derived d2('c');
    cout << "Derived d3 = d2 calls: " ;
    Derived d3 = d2;
    cout << "Derived d4 calls: ";
    Derived d4;
}

/* Output:
Derived d1(5) calls: Base(int)
Derived d1('c') calls: Base(char)
Derived d3 = d2 calls: Base(Base&)
Derived d4 calls: Base()*/

一般而言,当派生类未声明新数据成员或构造函数时,最好使用继承构造函数。

如果类型指定基类,则类模板可以从类型参数继承所有构造函数:

template< typename T >
class Derived : T {
    using T::T;   // declare the constructors from T
    // ...
};

如果基类的构造函数具有相同签名,则派生类无法从多个基类继承
https://zh.cppreference.com/w/cpp/language/using_declaration

你可能感兴趣的:(C++)