在传统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中继承来的候选构造函数有如下一些:
相应的,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