C++ 多态之继承4-派生类的构造函数以及初始化
来源:互联网 发布:linux 不保存退出vi 编辑:程序博客网 时间:2024/05/16 10:31
之前的两篇文章,我们已经了解了C++中继承的一些基本知识,也探索了初始化派生类的次序。本文主要是更深入的了解构造函数所扮演的角色(初始化一个派生类时)。我们继续沿用上篇文章的例子。
class Base{public: int m_nValue; Base(int nValue=0) : m_nValue(nValue) { }}; class Derived: public Base{public: double m_dValue; Derived(double dValue=0.0) : m_dValue(dValue) { }};
初始化非派生类时,构造函数只需要关心它自己的成员。例如,
int main(){ Base cBase(5); // use Base(int) constructor return 0;}当cBase被实例化时,发生如下事件:
1.为cBase预留内存
2.合适的Base构造函数被调用
3.初始化列表对变量进行初始化
4.执行构造函数的函数体
5.从调用出返回
这个过程非常的直观。但是换成初始化一个派生类时,事情变得稍微复杂点:
int main(){ Derived cDerived(1.3); // use Derived(double) constructor return 0;}当cDerived被实例化时,发生了如下事情:
1.为cDerived预留足够的内存(包括Base部分以及Derived部分)
2.合适的派生类构造函数被调用
3.Base对象被首先构造通过合适的Base构造函数
4.初始化列表对变量进行初始化
5.执行构造函数的函数体
6.从调用处返回
两者的唯一区别是实例化派生类时,基类的构造函数被先调用。Base构造函数建立对象的基类部分,然后返回到派生类的构造函数,接着派生类构造函数被允许执行它的工作。
初始化基类成员
当创建一个派生类对象时,我们该如何同时初始化m_dValue(派生类Derived的成员变量)和m_nValue(基类Base的成员变量)?
新程序员可能打算通过下列方法才解决问题:
class Derived: public Base{public: double m_dValue; Derived(double dValue=0.0, int nValue=0) <strong>// does not work</strong> : m_dValue(dValue), m_nValue(nValue) { }};这是个好的设想,点子是对的。我们要明确的给构造函数加上一个参数,否则C++将无法知道该给m_nValue初始化为何值。
然而,C++不允许在构造函数的初始化列表中对基类成员进行初始化。换句话说,构造函数的初始化列表只能初始化属于自己的成员变量。
那C++为什么要这样做呢?答案和const以及reference变量有关。设想下,如果m_nValue是一个const变量,将发生什么。因为const变量必须被初始化当它被创建的时候,基类构造函数必须给它赋值。然而,当基类构造函数执行完后,派生类的构造函数初始化列表会被执行。如果这样的话,每一个派生类的都有可能初始化这个变量并可能改变这个值。
所以上述的例子无法工作,因为m_nValue来自Base基类,而只有非继承的变量才能在初始化列表中被改变。
有些新程序员可能会使用如下方法来初始化基类成员变量:
class Derived: public Base{public: double m_dValue; Derived(double dValue=0.0, int nValue=0) : m_dValue(dValue) { m_nValue = nValue; }};当m_nValue是非const变量时,上面的办法可行,但当m_nValue为const变量事,则行不通。因为m_nValue被赋值了2次:1次实在Base基类的成员初始化列表中,1次是在派生类的构造函数体内。
庆幸的是,C++赋予我们选择执行哪个基类构造函数的的能力,通过在派生类初始化成员列表中调用指定基类构造函数来实现:
class Derived: public Base{public: double m_dValue; Derived(double dValue=0.0, int nValue=0) : Base(nValue), // Call Base(int) constructor with value nValue! m_dValue(dValue) { }};测试代码如下:
int main(){ Derived cDerived(1.3, 5); // use Derived(double) constructor return 0;}基类构造函数Base(int)将被用来初始化m_nValue为5,派生类构造函数将m_dValue初始化为1.3.
更详细的,发生了如下事情:
1.分配内存给cDerived
2.Derived(double, int) 构造函数被调用,dValue=1.3, nValue=5
3.编译器会检查我们是否要求执行指定基类构造函数。我们指定了!所以将调用 Base(int)参数值为5
4.基类构造函数初始化列表给m_nValue赋值5
5.执行基类构造函数体
6.基类构造函数返回
7.派生类构造函数初始化列表给m_dValue赋值1.3
8.执行派生类构造函数体
9.派生类构造函数返回
上面列出了9条,看起来挺复杂的,其实很简单。发生的所有事情就是派生类构造函数调用了指定的基类构造函数来初始化派生类对象的基类部分。因为m_nValue存在于对象的基类部分,基类构造函数是唯一能对其进行初始化的。
来看看另外一个实例:
#include <string>class Person{public: std::string m_strName; int m_nAge; bool m_bIsMale; std::string GetName() { return m_strName; } int GetAge() { return m_nAge; } bool IsMale() { return m_bIsMale; } Person(std::string strName = "", int nAge = 0, bool bIsMale = false) : m_strName(strName), m_nAge(nAge), m_bIsMale(bIsMale) { }}; // BaseballPlayer publicly inheriting Personclass BaseballPlayer : public Person{public: double m_dBattingAverage; int m_nHomeRuns; BaseballPlayer(double dBattingAverage = 0.0, int nHomeRuns = 0) : m_dBattingAverage(dBattingAverage), m_nHomeRuns(nHomeRuns) { }};BaseballPlayer类只初始化了自己的成员,并没有调用指定的Person构造函数,这意味着每个BaseballPlayer将使用默认Person构造函数,它初始化name为空,age为0。这并不符合我们的意图,修改后的BaseballPlayer类如下:
// BaseballPlayer publicly inheriting Personclass BaseballPlayer : public Person{public: double m_dBattingAverage; int m_nHomeRuns; BaseballPlayer(std::string strName = "", int nAge = 0, bool bIsMale = false, double dBattingAverage = 0.0, int nHomeRuns = 0) : Person(strName, nAge, bIsMale), // call Person(std::string, int, bool) to initialize these fields m_dBattingAverage(dBattingAverage), m_nHomeRuns(nHomeRuns) { }};测试代码如下:
int main(){ BaseballPlayer cPlayer("Pedro Cerrano", 32, true, 0.342, 42); return 0;}更详细的测试代码如下:
int main(){ BaseballPlayer cPlayer("Pedro Cerrano", 32, true, 0.342, 42); using namespace std; cout << cPlayer.m_strName << endl; cout << cPlayer.m_nAge << endl; cout << cPlayer.m_nHomeRuns; return 0;}结果:
Pedro Cerrano3242
继承链
实例:
#include <iostream>using namespace std; class A{public: A(int nValue) { cout << "A: " << nValue << endl; }}; class B: public A{public: B(int nValue, double dValue) : A(nValue) { cout << "B: " << dValue << endl; }}; class C: public B{public: C(int nValue, double dValue, char chValue) : B(nValue, dValue) { cout << "C: " << chValue << endl; }}; int main(){ C cClass(5, 4.3, 'R'); return 0;}结果:
A: 5B: 4.3C: R析构函数
当一个派生类被销毁时,析构函数的调用次序与构造函数完全相反。在上面的例子中,如果cClass被销毁,C的析构函数被最先调用,然后是B的,最后是A的。你可以自己写测试程序来验证。
- C++ 多态之继承4-派生类的构造函数以及初始化
- 多重继承派生类的构造函数
- 多重继承派生类的构造函数
- 多重继承派生类的构造函数
- 派生类构造函数-多继承
- C++ 基类构造函数带参数的继承方式及派生类的初始化
- 【C++继承与派生之二】有子对象的派生类的构造函数
- 继承与派生:派生类的构造函数
- 继承与派生:虚基类及其派生类的构造函数
- 三十九、继承与派生:派生类的构造函数****
- 鸡啄米:C++编程入门系列之三十九(继承与派生:派生类的构造函数)
- C++构造函数 & 拷贝构造函数 & 派生类的构造函数 & 虚继承的构造函数
- C++构造函数 & 拷贝构造函数 & 派生类的构造函数 & 虚继承的构造函数
- C++构造函数 & 拷贝构造函数 & 派生类的构造函数 & 虚继承的构造函数
- C++(继承时的名字遮蔽&&派生类的构造函数)
- c++多态之继承3-派生类的构造次序
- 派生类的构造函数及其对象的初始化
- 派生类的构造函数的初始化列表问题
- android获取热点主机ip和连接热点手机ip
- 博客是个好东西
- Linux+javaEE学习之文件的压缩+解压缩+java网络编程
- bug追踪
- Please verify that your device’s clock is properly set, and that your signing certificate is not exp
- C++ 多态之继承4-派生类的构造函数以及初始化
- Can't load IA 32-bit .dll on a AMD 64-bit platform
- Method not found: '!!0[] System.Array.Empty()'.
- word中将彩色图片转为灰度图
- iOS开发之如何在NSURLRequest中设置自定义header请求头
- linux 命令学习(二)
- 【PHP】PHP include()和require()方法的区别
- Java基础系列五、类+包
- Linux Samba服务器的建立