【C/C++基础进阶系列】C/C++ 对象模型 -- 对象构造语义(一)
发布日期:2021-05-07 20:53:20 浏览次数:25 分类:精选文章

本文共 8599 字,大约阅读时间需要 28 分钟。

【C/C++基础进阶系列】C/C++ 对象模型 -- 对象构造语义(一)

【1】对象构造函数语义 -- 编译器生成"合成默认构造函数"

#include 
using namespace std;class MATX{public: MATX() //默认构造函数 { cout << "goodHAHAHA" << endl; }};class MBTX{public: int m_i; int m_j; void funct() { cout << "Iamverygood" << endl; }};int main(){ MBTX myb; return 0;}/** * 结果 * 编译器没有为 MBTX 生成合成默认构造函数 */
#include 
using namespace std;class MATX{public: MATX() //默认构造函数 { cout << "goodHAHAHA" << endl; }};class MBTX{public: int m_i; int m_j; void funct() { cout << "Iamverygood" << endl; }public: MATX ma; //类类型成员变量};int main(){ MBTX myb; // goodHAHAHA return 0;}/** * 结果 * 编译器为 MBTX 生成合成默认构造函数 * 并且调用了 MATX 的构造函数 */
#include 
using namespace std;class M0TX{public: M0TX()//默认构造函数 { cout << "Ogood!" << endl; }};class MATX{public: MATX() //默认构造函数 { cout << "goodHAHAHA" << endl; }};class MBTX{public: int m_i; int m_j; void funct() { cout << "Iamverygood" << endl; }public: MATX ma; //类类型成员变量 M0TX m0; //注意定义顺序,先定义的ma,再定义的m0};int main(){ MBTX myb; // goodHAHAHA, Ogood return 0;}/** * 结果 * 编译器为 MBTX 生成合成默认构造函数 * 并且依次调用了 MATX M0TX 的构造函数 */
#include 
using namespace std;class MBTXPARENT{public: MBTXPARENT() { cout << "MBTXPARENT()构造函数执行了" << endl; }};class MBTX:public MBTXPARENT{public: int m_i; int m_j; void funct() { cout << "Iamverygood" << endl; }};int main(){ MBTX myb; // MBTXPARENT()构造函数执行了 return 0;}/** * 结果 * 编译器为 MBTX 生成合成默认构造函数 * 并且调用了 MBTXPARENT 的构造函数 */
#include 
using namespace std;class MBTX{public: int m_i; int m_j; void funct() { cout << "Iamverygood" << endl; } virtual void mvirfunc() //虚函数 { cout << "mvirfunc" << endl; }};int main(){ MBTX myb; return 0;}/** * 结果 * 编译器为 MBTX 生成合成默认构造函数 * 并且在生成的构造函数中插入代码 * 用于将类 MBTX 的虚函数表地址赋给对象的虚函数表指针 * (可以将虚函数表指针看成是一个隐藏的成员变量,属于类对象) */
#include 
using namespace std;class MBTXPARENT{public: MBTXPARENT() { cout << "MBTXPARENT()构造函数执行了" << endl; }};class MBTX:public MBTXPARENT{public: int m_i; int m_j; // 默认构造函数 MBTX() { m_i = 15; } void funct() { cout << "Iamverygood" << endl; } virtual void mvirfunc() //虚函数 { cout << "mvirfunc" << endl; }};int main(){ MBTX myb; return 0;}/** * 结果 * 编译器向 MBTX 的默认构造函数中加入了代码 * 1. 调用父类构造函数 MBTXPARENT::MBTXPARENT * 2. MBTX 类中存在虚函数,将 MBTX 类的虚函数表地址赋值给该类对象的虚函数表指针 */
#include 
using namespace std;class Grand //爷爷类{public:};class A : virtual public Grand //注意virtual {public:};class A2 : virtual public Grand //注意virtual {public:};class C :public A, public A2 //这里不需要virtual{public: C() //默认 构造函数 { cout << "C::C()默认构造函数执行了" << endl; }};int main(){ C cc; return 0;}/** * 结果 * 编译器会为子类和父类都生成合成默认构造函数 */
#include 
using namespace std;class Time{public: int Second{ 10 };};int main(){ Time mytime; return 0;}/** * 结果 * 编译器会为该类生成合成默认构造函数 */

结论

  • 1. 编译器生成“合成默认构造函数”的第一种情况
    • 1). 该类没有任何构造函数
    • 2). 该类包含一个类类型成员变量
    • 3). 该类包含的类类型成员变量所属的类有一个默认的构造函数
  • 2. 编译器生成“合成默认构造函数”的第二种情况
    • 1). 父类带有默认构造函数
    • 2). 子类没有任何构造函数
  • 3. 编译器生成“合成默认构造函数”的第三种情况
    • 1). 一个类含有虚函数
    • 2). 该类没有任何构造函数
  • 4. 编译器生成“合成默认构造函数”的第四种情况
    • 若一个类带有虚基类,编译器会为其生成一个合成默认构造函数
  • 5. 编译器生成“合成默认构造函数”的第五种情况
    • C++ 11 新特性,在定义成员变量时赋初值,编译器会为该类生成一个合成默认构造函数

【2】对象构造函数语义 -- 成员初始化列表

#include 
using namespace std;class Base{public: int ba; int bb; Base(int tmpa, int tmpb) // 构造函数 { }};class CSub{public: CSub(int tmpvalue) { }};class A :public Base{public: int m_x; int m_y; int &m_yy; const int m_myc; CSub cmysub; A(int& tmpvalue) :m_yy(tmpvalue), m_myc(tmpvalue), Base(tmpvalue, tmpvalue), cmysub(tmpvalue) { //m_yy = tmpvalue; // 错误,不可以,必须在初始化列表中进行绑定 m_x = 0; m_y = 0; m_yy = 160; }};int main(){ { int abc = 1; A a(abc); } return 0;}
#include 
using namespace std;class X{public: int m_i; X(int value = 0) :m_i(value) //类型转换构造函数 { printf("this = %p", this); cout << "X类的X(int)构造函数被调用" << endl; } X(const X& tmpv) { printf("this = %p", this); cout << "X类的拷贝构造函数被调用" << endl; } X& operator=(const X& tmpTime) { printf("this = %p", this); cout << "X类的拷贝赋值运算符被调用" << endl; return *this; //返回一个该对象的引用 } ~X() { printf("this = %p", this); cout << "X类的析构函数被调用" << endl; }};class XA{public: X xobj; int m_test; XA(int tmpvalue) { // xobj 的初始化放在了 XA 类的构造函数中 xobj = 1000; // 针对 xobj = 1000; 编译器的角度 // X tmpobj; // 生成一个临时对象 // tmpobj.X::X(1000); // 临时对象调用构造函数 // xobj.X::operator=(tmpobj); // 调用 xobj 的拷贝复制运算符 // tmpobj.X::~X(); // 调用析构函数销毁临时对象 m_test = 500; }};int main(){ { // 在执行类 XA 构造函数的函数体之前便构造了 xobj 对象 // 调用了类 X 的构造函数 // 编译器角度 // X xobj; // xobj.X::X(); XA myaobj(1000); } return 0;}/** * 结果 * this = 00B3F9B4X X类的X(int)构造函数被调用 * this = 00B3F9B8X X类的X(int)构造函数被调用 * this = 00B3F9B4X X类的拷贝赋值运算符被调用 * this = 00B3F9B8X X类的析构函数被调用 * this = 00B3F9B4X X类的析构函数被调用 */
#include 
using namespace std;class X{public: int m_i; X(int value = 0) :m_i(value) { printf("this = %p", this); cout << "X类的X(int)构造函数被调用" << endl; } X(const X& tmpv) { printf("this = %p", this); cout << "X类的拷贝构造函数被调用" << endl; } X& operator=(const X& tmpTime) { printf("this = %p", this); cout << "X类的拷贝赋值运算符被调用" << endl; return *this; //返回一个该对象的引用 } ~X() { printf("this = %p", this); cout << "X类的析构函数被调用" << endl; }};class XA{public: X xobj; int m_test; XA(int tmpvalue) : xobj(1000) { m_test = 500; }};int main(){ { // 编译器角度 // X xobj; // xobj.X::X(); XA myaobj(1000); } return 0;}// 对于类类型成员变量的初始化,放在构造函数的初始化列表中进行初始化比放在构造函数中初始化效率更高/** * 结果 * this = 00B3F9B4X X类的X(int)构造函数被调用 * this = 00B3F9B8X X类的析构函数被调用 */

结论

成员初始化列表

  • 1. 若成员变量是一个引用类型则必须在初始化列表中初始化
  • 2. 若成员变量是一个 const 类型则必须在初始化列表中初始化
  • 3. 若该类继承自一个父类,且父类中仅仅拥有带有参数的构造函数,则必须在初始化列表中初始化
  • 4. 若该类具有类类型成员变量,该类类型成员变量所指类的构造函数带有参数,则必须在初始化列表中初始化
  • 5. 对于类类型成员变量的初始化,放在构造函数的初始化列表中进行初始化比放在构造函数中初始化效率更高

成员初始化列表说明

  • 1. 初始化列表中的代码可看作被编译器按插入构造函数中的
  • 2. 初始化列表中的代码在构造函数体执行之前被执行
  • 3. 初始化列表中的成员变量的初始化顺序由变量在类中定义的顺序决定,而不是在初始化列表中出现的顺序

【3】对象构造函数语义 -- 继承体系下对象的构造

#include 
#include
using namespace std;class A{public: A() { printf("A::A()执行了,this = %p\n",this); }; virtual ~A() {}};class B : public A{public: B() { printf("B::B()执行了,this = %p\n", this); }; virtual ~B() {}};class C :public B{public: C() : m_c(11) { printf("C::C()执行了,this = %p\n", this); }; virtual ~C() {}public: int m_c;};// 继承体系下的对象构造// 对象的构造顺序 : 从父类到子类,从根源到末端int main(){ // A::A()执行了,this = 0075FE8C // B::B()执行了,this = 0075FE8C // C::C()执行了,this = 0075FE8C // 调用关系 /** * C::C() * B::B() * A::A() * printf("A::A()执行了,this = %p\n",this); * printf("B::B()执行了,this = %p\n", this); * vptr = C::vftable; // 给虚函数表指针赋予的最终值 * m_c = 1; // 成员初始化列表 * printf("C::C()执行了,this = %p\n", this); */ C c; return 0;}
#include 
#include
using namespace std;class A{public: A() { printf("A::A()执行了,this = %p\n",this); }; virtual ~A() {}public: virtual void myvirfunc() { myvirfunc2(); printf("A::myvirfunc()执行了!\n"); }; virtual void myvirfunc2() { printf("A::myvirfunc2()执行了!\n"); };};class B : public A{public: B() { printf("B::B()执行了,this = %p\n", this); }; virtual ~B() {}public: virtual void myvirfunc() { // 对于 myvirfunc2 的调用是通过虚函数表进行的 // 注意当前虚函数表指针指向的是哪个虚函数表 myvirfunc2(); printf("B::myvirfunc()执行了!\n"); }; virtual void myvirfunc2() { printf("B::myvirfunc2()执行了!\n"); };};class C :public B{public: C() : m_c(11) { /** * 类的构造函数中调用虚函数并不会通过调用虚函数表来调用 * 在构造函数中调用的虚函数从所在类往根类回溯,直接调用优先确定了的虚函数 */ myvirfunc(); printf("C::C()执行了,this = %p\n", this); }; virtual ~C() {}public: int m_c;public: virtual void myvirfunc() { myvirfunc2(); printf("C::myvirfunc()执行了!\n"); }; virtual void myvirfunc2() { printf("C::myvirfunc2()执行了!\n"); };};// 继承体系下的对象构造// 构造函数中对虚函数的调用// 建议// 不要在类的构造函数中调用虚函数,不要在类的析构函数中调用虚函数int main(){ C* mycobj = new C(); mycobj->myvirfunc(); delete mycobj; return 0;}

【4】对象析构函数语义

#include 
#include
using namespace std;class JI{public: JI() { cout << "JI::JI()缺省构造函数执行了" << endl; }public: virtual ~JI() { cout << "JI::~JI()析构函数执行了" << endl; }};class A : public JI{public: int m_i, m_j;public: A& operator=(const A& tmp) { m_i = tmp.m_i; m_j = tmp.m_j; cout << "A::operator=(const A&)拷贝赋值运算符执行了" << endl; return *this; } A(const A& tmptime) { m_i = tmptime.m_i; m_j = tmptime.m_j; cout << "A::A(const A&)拷贝构造函数执行了" << endl; } A() { cout << "A::A()缺省构造函数执行了" << endl; }};class ParC{public: virtual ~ParC() { cout << "ParC::~ParC()虚析构函数执行了" << endl; }};class MemC{public: ParC m_j;};// 析构函数语义// 若一个类没有定义析构函数,则生成的目标文件中也不会有该类的析构函数// 析构函数的合成// 1. 若当前类继承一个基类,基类带有析构函数,则编译器会为当前类合成一个析构函数// 先执行子类的析构函数在执行父类的析构函数// 2. 若当前类的成员变量是一个类类型变量,且该类类型带有析构函数,则编译器会为当前类合成一个析构函数// 先执行 MemC 的析构函数再执行 ParC 的析构函数int main(){ A aobj; MemC mobj; return 0;}

参考致谢

本博客为博主学习笔记,同时参考了网上众博主的博文以及相关专业书籍,在此表示感谢,本文若存在不足之处,请批评指正。

【1】C++ 新经典

【2】C++11/14 高级编程 Boost 程序库探秘

上一篇:【C/C++基础进阶系列】C/C++ 对象模型 -- 对象构造语义(二)
下一篇:【C/C++基础进阶系列】C/C++ 对象模型 -- 对象语义

发表评论

最新留言

不错!
[***.144.177.141]2025年04月03日 00时44分04秒