C++对象模型之对象构造语义学

本文详细探讨了C++中对象的构造顺序,从父类到子类,局部和全局对象的构造与析构,局部静态对象的生命周期,以及临时对象在拷贝构造和拷贝赋值中的应用。通过实例分析,揭示了临时对象的创建、使用和销毁过程,强调了局部静态对象只构造一次的特点,并提醒开发者注意临时对象的生命周期管理,以避免不必要的内存泄漏或错误行为。
摘要由CSDN通过智能技术生成

前言

本次笔记记录如下知识点

1、对象的构造顺序
2、局部对象和全局对象的构造和析构
3、局部静态对象,对象数组的构造和析构
4、临时性对象的详细探讨

一、对象的构造顺序

  • 对象的构造是从父类到子类,从跟源到末端。如下例子所示:
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
{
private:
	int m_c;
public:
	C():m_c(6)
	{
		printf("m_c = %d\n", m_c);
		printf("C::C()执行了,this = %p\n", this);
		
	}
	virtual ~C() {};
};
  
int main()
{
	C c;
	return 0;
}

运行结果如下
在这里插入图片描述

  • 通过上面例子我们可以得出,在运行C::C()的构造函数时,是先运行了B::B()的构造函数,在运行B::B()的构造函数时,先运行了A::A()的构造函数。
  • 初始化列表执行是在执行完B类的构造函数之后,执行完初始化列表在执行的C类构造函数。
  • C类对象中虚函数表指针赋值的流程是:先赋值A类虚函数表,然后赋值B类虚函数表,最后给的是C类的虚函数表。

二、局部对象和全局对象的构造和析构

局部对象的析构
  1. 局部对象的析构在任何一个地方的return之后都会调用,所以一般局部对象是现用现定义,尽量把对象定义在需要立即用到它的代码段附近。
  2. 局部对象的内部的成员变量的值都是随机的。所以尽量在构造函数初始化列表或者构造函数中为成员变量赋初值。
全局对象的构造和析构
  1. 编译程序时,会发现进入到主函数main之前,这个全局对象就已经生成了
  2. 全局对象时保存在数据段里面打的,全局变量里面的成员变量一般都是默认值0.
  3. 全局对象的内存地址是在编译器阶段确定的,所以每次程序执行,这些全局对象的内存地址都是相同的。

三、局部静态对象,对象数组的构造和析构

局部静态对象的构造和析构

通过如下例子来分析

class A 
{
public:
	A()
	{
		printf("A::A()缺省构造函数执行了\n");
	};
	~A()
	{
		printf("A::~A()析构构造函数执行了\n");
	}
};

void myfunc()
{
	static A s_aobj1;
	static A s_aobj2;
	printf("s_aobj1的地址是 %p\n", &s_aobj1);
	printf("s_aobj1的地址是 %p\n", &s_aobj2);
}

int main()
{
	myfunc();
	myfunc();
	return 0;
}

运行结果如下
在这里插入图片描述

  • 通过上面例子分析,局部静态对象的析构和构造只执行一次,而且它的析构调用是在main函数执行完成之后。
  • 局部静态对象的地址是在编译的时候确定好了的。
局部静态对象数组的构造和析构
  • 局部静态对象数组的构造与析构和局部静态对象是一样的。
  • 局部静态对象数组的内存分配:只有当局部静态对象数组在使用的时候才会给具体大小的内存空间。

四、临时性对象的详细探讨

拷贝构造产生的临时对象
  • 通过如下例子来分析
class A {
public:
	A()
	{
		cout << "A::A()构造函数执行" << endl;
	};
	A(const A& tmpobj)
	{
		cout << "A::A()拷贝构造函数执行" << endl;
	};
	~A()
	{
		cout << "A::~A()析构函数执行" << endl;
	};
};

A operator+ (const A& obj1, const A& obj2)
{
	A tmpobj;
	cout << "----------------" << endl;
	return tmpobj;
}

int main()
{
	A myonj1;
	A myonj2;
	A resultobj = myonj1 + myonj2;
	return 0;
}

运行结果如下
在这里插入图片描述

从上面代码分析,发现operator+ 的return tmpobj;代码行产生了一个临时对象,因为结果中看到了一次拷贝构造函数,这个临时对象就是通过tmpobj拷贝构造产生的,用来放置从opertor+ 返回的结果。

拷贝赋值运算符产生的临时性对象
  • 通过下面例子来分析
class A {
public:
	A()
	{
		cout << "A::A()构造函数执行" << endl;
	};
	A(const A& tmpobj)
	{
		cout << "A::A()拷贝构造函数执行" << endl;
	};
	~A()
	{
		cout << "A::~A()析构函数执行" << endl;
	};
	A& operator = (const A& obj)
	{
		cout << "A:: operator = () 拷贝赋值运算符执行" << endl;
		return *this;
	}
};

A operator+ (const A& obj1, const A& obj2)
{
	A tmpobj;
	cout << "----------------" << endl;
	return tmpobj;
}

int main()
{
	
	A myobj1;
	A myobj2;
	A resultobj;
	resultobj = myobj1 + myobj2;
	return 0;
}

运行结果如下
在这里插入图片描述

从上面的代码分析,可以发现operator + 中的return tmpobj;返回的临时对象调用了拷贝赋值运算符赋给了resultobj,然后这个临时对象自己析构掉了。具体调用的代码如下:

A:: operator = () 拷贝赋值运算符执行
A::~A()析构函数执行
直接运算产生的临时性对象
  • 通过如下例子来分析
class A {
public:
	int m_i;
	A()
	{
		m_i = 10;
		cout << "A::A()构造函数执行" << endl;
	};
	A(const A& tmpobj)
	{
		cout << "A::A()拷贝构造函数执行" << endl;
	};
	~A()
	{
		cout << "A::~A()析构函数执行" << endl;
	};
	A& operator = (const A& obj)
	{
		cout << "A:: operator = () 拷贝赋值运算符执行" << endl;
		return *this;
	}
};

A operator+ (const A& obj1, const A& obj2)
{
	A tmpobj;
	tmpobj.m_i = obj1.m_i + obj2.m_i;
	return tmpobj;
}

int main()
{
	
	A myobj1;
	A myobj2;
	if ((myobj1 + myobj2).m_i > 3)
	{ 
		cout << "条件成立" << endl;
	}
	return 0;
}

运行结果

在这里插入图片描述

  • 通过上面的代码分析,可以发myobj1 + myobj2这个代码执行了两次析构,其中一次是operator + 内部对象的析构,还有一次就是这个代码运算之后产生的对象直接被析构了。
  • 所以我们在写代码的时候要注意以下方式,其中(string(“123”) + string(“456”))这个临时对象会被析构掉。
const char *p   = (string("123") + string("456")).c_str();
printf("p = %s\n",p);
  • 如果将上面的代码修改一下,将临时对象绑定到引用上,那么这个临时对象的生命周期就会变成这个引用的生命周期了,这样就可以被保留。
const string& p   = (string("123") + string("456"));
printf("p = %s\n",p.c_str());
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值