做个实验,看一下成员变量的构造析构,父类子类的构造析构,以及虚函数对调用的影响。

 #include <iostream>
using namespace std; class Member
{
public:
Member(int n):m_n1(n)
{
cout<<"Member::Member("<<m_n1<<")"<<endl;
}
~Member()
{
cout<<"Member::~Member("<<m_n1<<")"<<endl;
}
private:
const int m_n1;
}; class Base
{
public:
Base():m_m1()
{
cout<<"Base::Base()"<<endl;
OnConstruct();
}
~Base() //这里目前不是虚函数
{
cout<<"Base::~Base()"<<endl;
OnDistruct();
}
virtual void OnConstruct()
{
cout<<"Base::OnConstruct()"<<endl;
}
virtual void OnDistruct()
{
cout<<"Base::OnDistruct()"<<endl;
}
virtual void Foo1()
{
cout<<"Base::Foo1()"<<endl;
}
void Foo2()
{
cout<<"Base::Foo2()"<<endl;
} private:
Member m_m1;//这是个类对象
}; class Drived:public Base
{
public:
Drived():m_m2()
{
cout<<"Drived::Drived()"<<endl;
OnConstruct();
}
~Drived()
{
cout<<"Drived::~Drived()"<<endl;
OnDistruct();
}
virtual void OnConstruct()
{
cout<<"Drived::OnConstruct()"<<endl;
}
virtual void OnDistruct()
{
cout<<"Drived::OnDistruct()"<<endl;
}
virtual void Foo1()
{
cout<<"Drived::Foo1()"<<endl;
}
void Foo2()//这个不是虚函数
{
cout<<"Drived::Foo2()"<<endl;
}
private:
Member m_m2;//这是个类对象
}; int main(int argc, char *argv[])
{
Base* p = new Drived;
p->Foo1();
p->Foo2();
delete p;
return ;
}

这段代码的运行输出为:

 Member::Member()  //父类的初始化列表被执行
Base::Base() //父类构造
Base::OnConstruct()//父类构造中只会调用父类的函数。父类构造完毕
Member::Member() //子类构造的初始化列表
Drived::Drived() //子类构造
Drived::OnConstruct()//子类构造中只会调用子类的函数。子类构造完毕
Drived::Foo1()//发生多态,调用子类重写的函数
Base::Foo2() //未多态,调用父类版本的函数
Base::~Base() //父类开始析构
Base::OnDistruct()//父类析构只会调用父类中的函数
Member::~Member()//父类成员反初始化

同学们可以看到,子类的析构没有被执行,怎么让它能值执行呢?把父类的析构修饰为virtual即可:

     ~Base() //这里目前不是虚函数

这样在执行一遍,可以看到结果如果(注释出来的是新增的输出):

 Member::Member()
Base::Base()
Base::OnConstruct()
Member::Member()
Drived::Drived()
Drived::OnConstruct()
Drived::Foo1()
Base::Foo2()
Drived::~Drived() //子类析构,在父类之前执行
Drived::OnDistruct()//子类析构只会调用子类的函数
Member::~Member() //子类成员反初始化
Base::~Base()
Base::OnDistruct()
Member::~Member()

小结一下:

通过父类指针指向子类对象实现多态。

多态的时候,父类析构修饰为虚函数,以保证子类析构被调用。

构造顺序是:先父类后子类(这是最主要流程,后两条都再此前提下);初始化列表在构造前执行;构造函数中值调用本类的函数(无论是否为虚函数)。

析构顺序是:先子类后父类(注意虚析构);析构中只调用本类的函数;本类析构后再析构初始化列表中的成员。

												

C++中多态中构造函数与析构函数的调用的更多相关文章

  1. C++C++中构造函数与析构函数的调用顺序

    http://blog.csdn.net/xw13106209/article/details/6899370 1.参考文献 参考1: C++继承中构造函数.析构函数调用顺序及虚函数的动态绑定 参考2 ...

  2. C++中构造函数和析构函数的调用顺序

    一般而言,析构函数调用的顺序和构造函数调用顺序相反,但是,对象的存储类别可以改变调用析构函数的顺序.举例说明: CreateAndDestroy类的定义 CreateAndDestroy类的成员函数的 ...

  3. C++学习笔记(7)----类的数组中构造函数和析构函数的调用顺序

    C++类的数组中构造函数和析构函数的调用顺序(2) 对于如下的代码: #include<iostream> using namespace std; class CBase { priva ...

  4. C++ 构造函数和析构函数的调用顺序、虚析构函数的作用

    构造函数和析构函数的调用顺序 构造函数的调用顺序: 当建立一个对象时,首先调用基类的构造函数,然后调用下一个派生类的构造函数,依次类推,直至到达最底层的目标派生类的构造函数为止. 析构函数的调用书序: ...

  5. C++类中函数(构造函数、析构函数、拷贝构造函数、赋值构造函数)

    [1]为什么空类可以创建对象呢? 示例代码如下: #include <iostream> using namespace std; class Empty { }; void main() ...

  6. C++中:默认构造函数、析构函数、拷贝构造函数和赋值函数——转

    对于一个空类,编译器默认产生4个成员函数:默认构造函数.析构函数.拷贝构造函数和赋值函数.1.构造函数:构造函数是一种特殊的类成员,是当创建一个类的时候,它被调用来对类的数据成员进行初始化和分配内存. ...

  7. C++继承,多重继承,虚继承的构造函数以及析构函数的调用顺序问题

    #include <iostream> using namespace std; class A{ int data_a; public: A(){ data_a = ; cout < ...

  8. C++:派生类的构造函数和析构函数的调用顺序

    一.派生类 在C++编程中,我们在编写一个基类的派生类时,大致可以分为四步: • 吸收基类的成员:不论是数据成员还是函数成员,派生类吸收除基类的构造函数和析构函数之外的全部成员. • 改造基类函数:在 ...

  9. 【C++】不要在构造函数或析构函数内调用虚函数

    这个问题来自于<Effective C++>条款9:永远不要在构造函数或析构函数中调用虚函数 . 假设有如下代码: class Transaction {// 所有交易的基类 public ...

  10. C++构造函数和析构函数的调用顺序

    1.构造函数的调用顺序 基类构造函数.对象成员构造函数.派生类本身的构造函数 2.析构函数的调用顺序 派生类本身的析构函数.对象成员析构函数.基类析构函数(与构造顺序正好相反) 3.特例 局部对象,在 ...

随机推荐

  1. PAT 乙级 1047 编程团体赛(20) C++版

    1047. 编程团体赛(20) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue 编程团体赛的规则为:每个参赛队由若 ...

  2. 廖雪峰Java1-2程序基础-9数组

    数组初识 1.数组的特点: 数组所有元素初始化默认值,int默认值为0 数组创建后大小不可改变 数组索引从0开始 数组是引用类型 使用索引下标访问数组元素,索引超出范围会报错 2.数组的定义: 类型[ ...

  3. jQuery的杂项

    <script  src="引入插件"></script> 位置应写在head中,在http协议中,当你浏览网页时,会先加载head里面的东西  刷新页面时 ...

  4. 给VMware下的Linux扩容磁盘空间到根分区(以centos7.0为例)

    一.扩展VMWare硬盘空间 关闭Vmware 的 Linux系统,这样,才能在VMWare菜单中设置: VM -> Settings... -> Hardware -> Hard ...

  5. [UE4]name slot一个种应用技巧

    如图所示“MouseOver”是一个Child Widget,是一个按钮. “Image_0”跟“MouseOver”是重叠在一起的,这样“Image_0”就会挡住“MouseOver”按钮的事件响应 ...

  6. GDB 调试 ---转 比较全的东东

    转自 程序人生:http://www.programlife.net/gdb-manual.html Linux 包含了一个叫gdb 的GNU 调试程序.gdb 是一个用来调试C和C++程序的强力调试 ...

  7. Linux安装jsvc,及Linux服务开发

    在linux上以服务的方式启动java程序,需要提前安装jsvc.linux是利用daemon(jsvc)构建java守护进程. 编译 daemon 安装JSVC 1 下载文件,http://comm ...

  8. 第1课 类型推导(1)_auto关键字

    1.  auto关键字 (1)auto的作用是让编译器自动推断变量的类型,而不需要显式指定类型.这种隐式类型的推导发生在编译期. (2)auto并不能代表实际的类型声明,只是一个类型声明的“占位符” ...

  9. 实用JS代码

    浏览器功能 1.0 浏览器判断 如下代码判断是手机还是电脑访问的网站 function IsPC () { var userAgentInfo = navigator.userAgent var Ag ...

  10. 不曾忘记的Vue.js

    马上2017年就结束了,虽然我因为目前的项目用不上你vue,但是我不曾忘记你,在时间缝隙的某一刹那,我再次将你拾起. vue.js全家桶:vue+ vuex+axios+vue-router+webp ...