隐式类型转换/匿名对象/拷贝和赋值/编译器优化

概念区分

本篇文章主要是对于隐式类型转换和匿名对象的理解,其次重点是为了区分拷贝构造和赋值的区别,并以此引出对应的编译器优化

1.隐式类型转换

构造函数不仅可以构造与初始化对象,对于接收单个参数的构造函数,还具有类型转换的作用

接收单个参数的构造函数具体表现:

  1. 构造函数只有一个参数
  2. 构造函数有多个参数,除第一个参数没有默认值外,其余参数都有默认值
  3. 全缺省构造函数

特别注明:C++11支持多参数的隐式类型转换

class Point
{
public:
	Point(int val)
		:_val(val)
	{
		cout << "Point(int val) -- 构造" << endl;
	}

	Point(const Point& p)
	{
		cout << "Point(const int& p) -- 拷贝构造" << endl;
		_val = p._val;
	}
	
	Point& operator=(const Point& p)
	{
		cout << "Point& operator=(const Point& p) -- 赋值" << endl;

		if (this != &p)
		{
			_val = p._val;
		}
	}

private:
	int _val;
};

int main() 
{
	// 正常方式 - 有名对象 - 生命周期在当前函数局部域
	Point p1(10);
	// 隐式类型转换 - C++支持单参数构造函数的隐式类型转换
	// 隐式类型转换生成的临时变量具有常性,所以引用要加const,引用传参也需要加const
	Point p2 = 10;
	const Point& p3 = 10;
	
	return 0;
}

隐式类型的转换作用体现在string

void Push_back(const string& s1)
{
	//
}

int main()
{
	// 如果不支持隐式类型转换,只能这样传参
	string s1("ZCDL");
	Push_back(s1);

	// 支持隐式类型转换 - 内部走了构造的隐式类型转换
	Push_back("ZCDL"); 

	return 0;
}

C++11支持多参数的隐式类型转换,注意:隐式类型转换的过程中会产生临时变量,可以直接传参,但是如果想要引用或者引用传参,那么需要加const

class Point
{
public:
	Point(int a1, int a2)
		:_a1(a1)
		,_a2(a2)
	{
		cout << "Point(int val) -- 构造" << endl;
	}

	Point(const Point& p)
	{
		cout << "Point(const int& p) -- 拷贝构造" << endl;
		_a1 = p._a1;
		_a2 = p._a2;
	}
	
	Point& operator=(const Point& p)
	{
		cout << "Point& operator=(const Point& p) -- 赋值" << endl;

		if (this != &p)
		{
			_a1 = p._a1;
			_a2 = p._a2
		}
	}

private:
	int _a1;
	int _a2
};


int main()
{
	Point p1(1, 2);

	Point p2 = { 1, 2 };

	const Point& p3 = { 1, 2 };
	
	return 0;
}

如果想要禁止隐式类型转换,就要在构造函数加关键字explicit,禁止类型转换

2.匿名对象

匿名对象是在隐式类型被禁用后有作用,其特点为生命周期仅在这一行

有名对象特点:生命周期在当前局部域
匿名对象特点:生命周期只在这一行

void Push_back(const string& s1)
{
	//
}

int main()
{
	// 如果不支持隐式类型转换,只能这样传参
	string s1("ZCDL");
	Push_back(s1);

	// 支持隐式类型转换 - 内部走了构造的隐式类型转换
	Push_back("ZCDL"); 

	// 当隐式类型转换被禁用,使用匿名对象
	Push_back(string("ZCDL"));

	return 0;
}

匿名对象也常用于题目中调用函数Solution(). - LeetCode

3.拷贝和赋值的区别

拷贝和赋值不能靠是否是=来判断,有等号不一定时赋值

拷贝还是赋值要通过被赋值对象是否初始化来确定,用已经初始化的对象去初始化未初始化的对象为拷贝构造,即使有等号,对象是未初始化的也是拷贝构造
当对象已经初始化,再用已经初始的对象给等号为赋值

class Point
{
public:
	Point(int a1)
		:_a1(a1)
	{
		cout << "Point(int val) -- 构造" << endl;
	}

	Point(const Point& p)
	{
		cout << "Point(const int& p) -- 拷贝构造" << endl;
		_a1 = p._a1;
	}
	
	Point& operator=(const Point& p)
	{
		cout << "Point& operator=(const Point& p) -- 赋值" << endl;

		if (this != &p)
		{
			_a1 = p._a1;
		}
	}

private:
	int _a1;
};

int main()
{
	// 构造
	Point p1(10);

	// 拷贝构造的两种方式:
	// 1. 拷贝构造 - p2未初始化,用初始化的p1去初始化p2为拷贝构造(即使有等号也为拷贝构造)
	Point p2 = p1;
	// 2. 拷贝构造 - 直接方式
	Point p3(p1);

	// 赋值 - p4已经初始化,再用p1去初始化为赋值
	Point p4(20);
	p4 = p1;

	return 0;
}

结论:判断依据不能仅靠等号去判断是赋值还是拷贝,要看=对面的对象是否初始化,如果初始化就为赋值,未初始化就为拷贝

4.编译器优化

编译器优化:构造+拷贝构造优化成直接构造,拷贝构造+拷贝构造优化成直接拷贝构造(传参返回的深拷贝会出先两次拷贝构造,一次是销毁产生临时对象时会拷贝构造一次,另一次是给赋值的时候会进行一次拷贝构造,两次都是深拷贝,防止内存泄漏)

条件:同时性,要在同一行,即构造和拷贝构造同时进行(同一行即可),才会发生优化(同时性)
赋值:不会发生优化,因为赋值一定是给初始化对象的,所以赋值的过程中不会出现构造(两个不能写在同一行,写在同一行为拷贝构造),所以赋值和构造一定是分开进行的,一定不会出现优化

// 默认生成拷贝构造,对于内置类型完成值拷贝,对于自定义类型(自己写的类就是自定义类型)去调用它的拷贝构造
class Point
{
public:
	Point(int a1)
		:_a1(a1)
	{
		cout << "Point(int val) -- 构造" << endl;
	}

	Point(const Point& p)
	{
		cout << "Point(const int& p) -- 拷贝构造" << endl;
		_a1 = p._a1;
	}
	
	Point& operator=(const Point& p)
	{
		cout << "Point& operator=(const Point& p) -- 赋值" << endl;

		if (this != &p)
		{
			_a1 = p._a1;
		}

		return *this;
	}

private:
	int _a1;
};

int main()
{
	// 构造 * 2
	Point p1(10);
	Point p2(20);
	// 赋值和构造一定是分开进行的, 先构造后赋值
	p1 = p2;

	// 拷贝构造 - 因为p1没有进行构造,只有p3的拷贝构造,编译器不会优化
	Point p3 = p1;

	// 构造+拷贝构造的过程就是隐式类型转换
	// 用10调用Point构造函数生成一个临时对象,再用这个对象去拷贝构造p3
	// 10进行了构造,生成了临时对象拷贝构造p3,编译器直接优化成构造‘
	// 同时性:构造和拷贝构造同时发生 - 优化成直接构造
	Point p4 = 10;

	return 0;
}

在这里插入图片描述

补充点:两次拷贝构造会被优化成直接拷贝构造,减少一次深拷贝,后续还会有更好的优化方法,为右值引用的移动语义(移动构造和移动赋值),可以将深拷贝直接优化成移动构造,减少内存的使用
两次拷贝构造的场景

End!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值