当前位置:网站首页>构造函数、类成员、析构函数调用顺序
构造函数、类成员、析构函数调用顺序
2022-06-30 11:28:00 【Adunn】
1. 构造函数、类成员、析构函数调用顺序
C++构造函数的调用次序是:
基类的成员->基类的构造函数体->派生类的成员->派生类的构造函数体
C++析构函数的调用次序是:
派生类的析构函数->派生类的成员析构->基类的析构函数->基类的成员析构
- 示例1:构造函数、类成员调用顺序
- 示例2:基类、派生类构造函数(析构函数)调用顺序
- 示例3:综合:基类、派生类构造函数(析构函数)、类成员调用顺序
- 示例4:综合:基类、派生类虚析构函数(virtual虚函数)调用顺序
1.1. 示例1:构造函数、类成员调用顺序
#include <iostream>
class ParentMember
{
public:
ParentMember()
{
std::cout<<"ParentMember Construct"<<std::endl;
}
~ParentMember()
{
std::cout<<"ParentMember Destruct"<<std::endl;
}
};
class Parent
{
public:
Parent()
{
std::cout<<"Parent Construct"<<std::endl;
}
~Parent()
{
std::cout<<"Parent Destruct"<<std::endl;
}
private:
ParentMember parentMember;
};
int main()
{
cout<<"Construct过程: "<<endl;
Parent *p = new Parent;
cout<<""<<endl;
cout<<"Destruct过程: "<<endl;
delete p;
}
运行结果:
Construct过程:
ParentMember Construct
Parent Construct
Destruct过程:
Parent Destruct
ParentMember Destruct
从运行结果不难看出:
1、在构造过程中,执行顺序为: 类成员->类的构造函数体。
若断点调试,会发现,首先运行到构造函数的 '{'处,就会掉头先调用成员变量了。
2、在析构过程中,执行顺序为:类的析构函数 -> 类的成员析构
1.2. 示例2:基类、派生类构造函数(析构函数)调用顺序
基类、派生类构造的顺序
- 基(base) -> 派生(derived);即先构造基类, 再构造派生类;
- 因为 基类 是独立于派生类的, 即不会调用派生类中的对象, 所以应该先被生成;
- 如果派生类先于基类生成, 则因为无法调用基类资源, 可能生成失败;
基类、派生类析构的顺序
- 派生(derived) -> 基(base); 即先释放派生类, 再释放基类;
- 因为 派生类 需要先释放调用的基类资源, 所以应该优先释放;
- 如果基类先析构, 则有可能某些资源被派生类占用, 可能导致析构失败;
#include <iostream>
class Parent
{
public:
Parent()
{
std::cout<<"Parent Construct"<< std::endl;
}
~Parent()
{
std::cout<<"Parent Destruct"<< std::endl;
}
};
class Child: public Parent
{
public:
Child()
{
std::cout<<"Child Construct"<< std::endl;
}
~Child()
{
std::cout<<"Child Destruct"<< std::endl;
}
};
int main()
{
std::cout<<"Construct过程: "<< std::endl;
Parent *p = new Child;
std::cout<<" "<< std::endl;
std::cout<<"Destruct过程: "<< std::endl;
delete p;
}
运行结果:
Construct过程:
Parent Construct
Child Construct
Destruct过程:
Parent Destruct
从运行结果不难看出:
1、在构造过程中,执行顺序为: 先基类,后子类,即:基类的构造函数体 -> 子类的构造函数体
若断点调试,会发现,首先运行到子类构造函数的 '{'处,就会掉头先调用基类成员变量了。
2、在析构过程中,执行顺序为: 怎么仅基类呢?
执行顺序本应该为:先子类,后基类,即子类的析构函数 -> 基类的析构函数。但实际运行效果我们不难发现,在最后释放指针p所指向的对象时,只释放了基类的部分,没有释放派生类的部分。这是因为,指针p声明时是基类的指针,而基类的析构函数不是虚函数,所以调用那个析构函数是在编译时确定的。如果要执行正确的析构顺序,需要将基类的析构函数定义为 virtual, 这样派生类的析构函数就自动是virtual的了,在最后释放指针p时,按照RTTI,执行p所指真实对象的析构函数。
class Parent
{
public:
Parent()
{
std::cout<<"Parent Construct"<< std::endl;
}
virtual ~Parent() //增加virtual
{
std::cout<<"Parent Destruct"<< std::endl;
}
};
修改后运行结果:
Construct过程:
Parent Construct
Child Construct
Destruct过程:
Child Destruct
Parent Destruct
1.3. 示例3:综合:基类、派生类构造函数(析构函数)、类成员调用顺序
#include <iostream>
class ParentMember
{
public:
ParentMember()
{
std::cout<<"ParentMember Construct"<< std::endl;
}
~ParentMember()
{
std::cout<<"ParentMember Destruct"<< std::endl;
}
};
class Parent
{
public:
Parent()
{
std::cout<<"Parent Construct"<< std::endl;
}
~Parent()
{
std::cout<<"Parent Destruct"<< std::endl;
}
private:
ParentMember parentMember;
};
class ChildMember
{
public:
ChildMember()
{
std::cout<<"ChildMember Construct"<< std::endl;
}
~ChildMember()
{
// std::cout<<"ChildMember Destruct"<< std::endl;
}
};
class Child: public Parent
{
public:
Child()
{
std::cout<<"Child Construct"<< std::endl;
}
~Child()
{
std::cout<<"Child Destruct"<< std::endl;
}
private:
ChildMember childMember;
};
int main()
{
std::cout<<"Construct过程: "<< std::endl;
和Parent *p = new Child;
std::cout<<""<< std::endl;
std::cout<<"Destruct过程: "<< std::endl;
delete p;
}
运行结果:
Construct过程:
ParentMember Construct
Parent Construct
ChildMember Construct
Child Construct
Destruct过程:
Parent Destruct
ParentMember Destruct
从运行结果不难看出,在构造过程中,执行顺序为:
- 先基类,后派生;
- 基类先成员、后构造函数体;
- 派生类先成员、后构造函数体;
- 总体调用顺序为:基类的成员->基类的构造函数体->派生类的成员->派生类的构造函数体;
- 若断点调试,会发现,首先运行到子类构造函数的 '{'处,就会掉头先调用基类成员变量了。
析构函数执行顺序未按预期执行,详见:示例2尾部详细说明。修改方案,详见:示例4.
1.4. 示例4:综合:基类、派生类虚析构函数(virtual虚函数)调用顺序 优化后方案
class Parent
{
public:
Parent()
{
std::cout<<"Parent Construct"<< std::endl;
}
virtual ~Parent() //增加virtual
{
std::cout<<"Parent Destruct"<< std::endl;
}
private:
ParentMember parentMember;
};
运行结果:
Construct过程:
ParentMember Construct
Parent Construct
ChildMember Construct
Child Construct
Destruct过程:
Child Destruct
//ChildMember Destruct //未输出???
Parent Destruct
ParentMember Destruct
不难看出以下规律:
C++构造函数的调用次序是:
基类的成员->基类的构造函数体->派生类的成员->派生类的构造函数体
C++析构函数的调用次序是:
派生类的析构函数->派生类的成员析构->基类的析构函数->基类的成员析构
边栏推荐
- ClipboardJS——开发学习总结1
- 关于IP定位查询接口的测评Ⅲ
- led背光板的作用是什么呢?
- 对象映射 - Mapping.Mapster
- 1175. prime permutation
- Qt嵌入子Qt程序窗口到当前程序
- [revisiting the classic C language] ~x,%c,%d,%x, etc. in C language, the role of the address character in C language, and the consortium in C language
- Typescript readonlyarray (read only array type) details
- dplyr 中的filter报错:Can‘t transform a data frame with duplicate names
- R language de duplication operation unique duplicate filter
猜你喜欢

Filter error in dplyr: can't transform a data frame with duplicate names

Is the golden cycle of domestic databases coming?

STM32F407ZGT6使用SDIO方式驱动SD卡

Our company has used this set of general solutions for 7 years, and has opened up dozens of systems, a stable batch!

Digitalization is not a trial, but a wading out of "Xingzhi Digital China" × History of Foxconn

Quel est le rôle du rétroéclairage LED?

A quietly rising domestic software, low-key and powerful!

Oracle netsuite helps TCM bio understand data changes and make business development more flexible

1175. prime permutation

led背光板的作用是什么呢?
随机推荐
wallys/3×3 MIMO 802.11ac Mini PCIe Wi-Fi Module, QCA9880, 2,4GHz / 5GHzDesigned for Enterprise
PointDistiller:面向高效紧凑3D检测的结构化知识蒸馏
There are so many kinds of coupons. First distinguish them clearly and then collect the wool!
[applet practice series] Introduction to the registration life cycle of the applet framework page
Limited time appointment | Apache pulsar Chinese developer and user group meeting in June
Qt嵌入子Qt程序窗口到当前程序
R语言查看版本 R包查看版本
数据库连接池 druid
Set up your own website (13)
STM32F407ZGT6使用SDIO方式驱动SD卡
Who still remembers "classmate Zhang"?
AMS source code analysis
相对位置编码Transformer的一个理论缺陷与对策
If it is not listed again, Kuangshi technology will not be able to endure
Automatic database growth
AutoCAD - len command
以PolarDB为代表的阿里云数据库以跻身全球第一阵营
深入解析 Apache BookKeeper 系列:第四篇—背压
TypeScript ReadonlyArray(只读数组类型) 详细介绍
CVPR 2022 | greatly reduce the manual annotation required for zero sample learning. Mapu and Beiyou proposed category semantic embedding rich in visual information