引入
在传统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<iostream>
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<iostream>
using namespace std;
struct A{A(int){}
};struct B : A{using A::A;
};int main(){B b; // error:B没有默认构造函数
}
总结
为此C++11利用关键字using
引入了继承构造函数的概念(当派生类要使用基类的成员函数时用)。
#include <iostream>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 <iostream>
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 Baseusing 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