临时对象、类外运算符重载
#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;
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/d119646fcff409c921a608e28a9af5f5.png)