构造方法是用来初始化类的对象,与父类的其他成员不同,在C++11之前,不能被子类继承(子类可以继承父类所有的成员变量和成员方法,但不继承父类的构造方法),因此,为了完成基类的初始化,需要在初始化列表中调用基类的构造函数,从而完成构造函数的传递,如果基类有多个构造函数,子类需要实现与基类相对应的(多个)构造函数。
如果没有显示的构造函数,编译器会给一个默认的构造函数,并且该默认的构造函数仅仅在没有显示的申明构造函数的情况下创建。
如下:
class Base
{
public:
Base(char c) : m_nIndex(0), m_cChar(c){}
Base(int a) : m_nIndex(a), m_cChar('0'){}
void display()
{
cout << m_nIndex << " " << m_cChar << " Base test..." << endl;
}
private:
int m_nIndex;
char m_cChar;
};
class SubClass : public Base
{
public:
SubClass(char c) : Base(c){}
SubClass(int a) : Base(a){}
void display()
{
// to do something...
Base::display();
cout << " test... " << endl;
}
};
由上面的例子可以看出,子类 SubClass 的实现中,由于继承了基类 Base,所以在类 SubClass 的构造函数的初始化列表中,调用了基类 Base 的构造函数,并且基类 Base 有几个构造函数,子类 SubClass 则相应的实现几个对应的构造函数,这样,假设基类 Base有很多个构造函数,则需要全部实现对应的构造方法,这种方法无疑增加了麻烦,降低效率。
而C++11中,出现了继承构造函数(Inheriting Constructor),使用关键字 using 来申明基类的构造函数。
如下:
class Base
{
public:
Base(char c) : m_nIndex(0), m_cChar(c){}
Base(int a) : m_nIndex(a), m_cChar('0'){}
void display()
{
cout << m_nIndex << " " << m_cChar << " Base test..." << endl;
}
private:
int m_nIndex;
char m_cChar;
};
class SubClass : public Base
{
public:
using Base::Base;
void display()
{
// to do something...
Base::display();
cout << m_nValue << " " << m_bTure << " test..." << endl;
}
private:
#if 0
int m_nValue{ 0 }; //C++11 之后可以使用的对类中的成员进行初始化的方法
bool m_bTure{ true }; //默认初始化为false
#else
int m_nValue;
bool m_bTure;
#endif
};
上面的例子,我们使用 using 关键字将基类 Base 的构造函数继承给了子类 SubClass,这样减少了编写子类构造函数来完成基类 Base 的初始化。
C++11 标准规定,继承构造函数与类的一些默认函数(默认构造、析构、拷贝构造函数等)一样,是隐式声明,如果一个继承构造函数不被相关代码使用,编译器不会为其产生真正的函数代码。
需要注意的是:
-
继承构造函数无法初始化子类的成员变量,比如上面例子中的 else 分支,非静态成员变量 m_nValue 和 m_bTure 未被进行初始化,而 if 分支,是C++11特性中支持的初始化成员变量的方法。
两次测试结果如下:
else分支:
if分支:
同样的,还可以通过新增子类构造函数的方法使用初始化列表进行初始化。
比如:
class SubClass : public Base
{
public:
using Base::Base;
SubClass(int a, bool b) : Base(a), m_nValue(a), m_bTure(false){}
void display()
{
// to do something...
}
private:
int m_nValue;
bool m_bTure;
};
- 基类构造函数有默认值时, 会产生多个默认构造函数,继承构造函数是不会继承基类构造函数中的默认值。
如下:
class Base
{
public:
Base(int a = 4, char c = 'a') : m_nIndex(a), m_cValue(c){}
void display()
{
cout << m_nIndex << " " << m_cValue << " Base test..." << endl;
}
private:
int m_nIndex;
char m_cValue;
};
class SubClass : public Base
{
public:
using Base::Base;
void display()
{
Base::display();
cout << m_nValue << " " << m_bTure << " test..." << endl;
}
private:
#if 0
int m_nValue{0}; //C++11 之后可以使用的对类中的成员进行初始化的方法
bool m_bTure{true}; //默认初始化为false,可以置顶初始值 bool m_bTure{true};
#else
int m_nValue;
bool m_bTure;
#endif
};
产生的默认构造函数:
Base()
Base(int a)
Base(char c)
Base(int a, char c)
Base(const Base& other)
相应的子类也会产生对应的构造函数。
运行上述代码的结果:
由上图可以看出,基类Base的成员变量全部由默认参数初始化,但是子类SubClass的成员变量没有继承基类的默认参数。出现了随机值。
因此,在基类的构造函数中有默认值时,要注意子类的成员变量进行初始化。
- 多继承的情况下,使用继承构造函数可能会出现编译错误。
class Base1
{
public:
Base1(int a) : m_nIndex(a){}
private:
int m_nIndex;
};
class Base2
{
public:
Base2(int a) : m_nIndex(a){}
private:
int m_nIndex;
};
class SubClass : public Base1, public Base2
{
public:
using Base1::Base1;
using Base2::Base2;
#if 0 //可避免编译错误的情况
SubClass(int a) : Base1(a), Base2(a){}
#endif
void display()
{
// to do something...
cout << m_nValue << " " << m_bTure << " test..." << endl;
}
private:
#if 0
int m_nValue{ 0 }; //C++11 之后可以使用的对类中的成员进行初始化的方法
bool m_bTure{ true }; //默认初始化为false
#else
int m_nValue;
bool m_bTure;
#endif
};
编译上述代码,出现:
出现的原因是:多个继承的时候,存在出现继承的构造可能在派生类中出现一样的情况(函数签名),避免出现这种错误的方法,使用显示定义构造函数的方法。
- 基类的构造函数被申明为私有,或者基类是重需基类,则不能进行申明继承构造函数。
以上代码使用测试用例:
int main(int argc, char *argv[])
{
SubClass* d = new SubClass(2);
d->display();
delete d;
d = NULL;
return 0;
}