2_3_13 临时对象、类外运算符重载、对象移动、noexcept

临时对象、类外运算符重载

#include "hjcommon.hpp"

using namespace std;
HJ_NS_USING

class A
{
public:
	int num = 0;
//	A(int a) : num(a) = default; // default不可以这么用
	A(int a) { cout << "one arg constructor." << endl; }
	A() { cout << "normal constructor." << endl; }
	A(const A &a) { cout << "copy constructor." << endl; }
	~A() { cout << "destructor." << endl; }
};

// 函数形参是引用string&则必须定义为const,否则编译器报错,但是定义为对象string则不用为const
// 由c语言字符串转换到string&时编译器会生成一个string临时对象,因为会产生一个临时对象,编译器为了防止程序员修改此临时变量,所以string&必须定义为const
// 结论:实际上时编译器只会为const引用产生临时对象,不会为非const引用产生,所以当string&不用const修饰时,编译器会报函数传参错误。。
//static A testTmp(String str) // 可以,用对象接收不会有上述考虑
//static A testTmp(String &str) // 不可以
static A testTmp(const String &str)
{
	A a;
	cout << hex << &a << endl; // 0x7ffc9634ee7c
	a.num = 3;
	return a; // 当函数返回时类的拷贝构造函数构造一个临时对象,但是ubuntu的c++编译器中,应该是优化了,直接使用了函数体中 A a; 临时对象作为返回对象
}
static A&& testRef() // 右值引用作函数返回
{
	A a;
	a.num = 5;
	return std::move(a);
}
// 类外运算符重载, 可以使类对象在重载的运算符的右边, 类内重载的运算符类对象只能在运算符左边
//A&& operator+(const int a, const A& b) // 不可以,可能会造成抛宕
//A& operator+(const int a, const A& b) // 不可以,可能会造成抛宕
//A operator+(const int a, const A& b) // 可以
static A&& operator+(const int a, const A& b) // 如果返回的类型是左值引用或右值引用,若此函数不使用static修饰,会抛宕。。 若返回的类型是A,那么没问题。。
{
	A ret;
	ret.num = a + b.num;
	return std::move(ret);
//	return ret;
}

int main_2_3_13(int argc, char *argv[])
{
	// 为了提高程序性能,应该尽量少产生临时对象, 临时对象是一种右值,可以使用右值引用接收,所以编码时无法避免的临时对象尽量使用右值引用传递
	char *s = "some";
	A a = testTmp(s); // 也可是使用 A && 右值引用接收, 临时对象是一种右值,可以使用右值引用接收
	cout << hex << &a << ", a.num=" << dec << a.num << endl; // 0x7ffc9634ee7c, a.num=3
	a = 1000; // 这种隐式类型转换,也会产生一个临时对象

	A &&b = testRef();
	cout << dec << "b.num=" << b.num << endl; // b.num=5
	A &&ret = 22 + b; // 会调用类外运算符重载 operator+
	cout << dec << "ret.num="<< ret.num << endl; // ret.num=27

	return 0;
}

对象移动、noexcept

#include "hjcommon.hpp"

using namespace std;
HJ_NS_USING

class A
{
public:
	A() { cout << "A no arg constructor." << endl; }
	A(const A& a, int num=0) { cout << "A copy constructor." << endl; } // 拷贝与移动构造函数若想增加参数,那么参数必须有默认值。拷贝与移动赋值函数只能有一个参数
	A(A&& a, int num=0) noexcept { cout << "A move constructor." << endl; } // 移动构造函数
	~A() { cout << "A destructor." << endl; }
};
class B
{
public:
	A *m_a;

	B() : m_a(new A()) { cout << "B no arg constructor." << endl; }
	B(const B& b) : m_a(new A(*(b.m_a))) { cout << "B copy constructor." << endl; } // B拷贝时,成员m_a也发生拷贝
	// 若未提供移动函数时,调用std::move时,编译器会调用拷贝构造函数
	B(B&& b) noexcept : m_a(b.m_a) // B移动时,成员m_a也移动,同时需要截断原内存拥有者
	{
		b.m_a = nullptr; // 截断原内存拥有者
		cout << "B move constructor." << endl;
	}
	~B()
	{
		delete m_a;
		cout << "B destructor." << endl;
	}

	B& operator=(const B &b) // 拷贝赋值函数
	{
		if (this == &b) return *this; // 同一个对象直接返回
		delete m_a;
		m_a = new A(*(b.m_a)); // 拷贝内存
		cout << "B copy operator=." << endl;
		return *this;
	}
	B& operator=(B &&b) noexcept // 移动赋值函数
	{
		if (this == &b) return *this; // 同一个对象直接返回
		delete m_a;
		m_a = b.m_a; // 移动内存
		b.m_a = nullptr; // 截断原内存拥有者
		cout << "B move operator=." << endl;
		return *this;
	}
};

static B getB()
{
	B b;
	return b;
}

int main(int argc, char *argv[])
{
	// 对象移动的概念 c++11引入
	B a = getB();
//	B &&b = getB(); // 此写法与 A a = getA(); 写法效果一样,前者是用引用接管函数返回,后者是用对象接管函数返回。 函数返回值是右值

	// noexcept c++11引入,用于修饰移动构造函数或移动赋值函数,无论在函数在声明与实现的地方都要加上 noexcept ,建议移动函数都用此修饰
	// 作用:通知标准库此移动构造函数不抛出任何异常(提高编译器工作效率)
	B c;
	c = a; // 拷贝赋值
	c = std::move(a); // 移动赋值

	return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值