一、拷贝构造函数
1、拷贝构造函数的语法:
拷贝构造函数名字(const 类名& ) {}
拷贝构造函数名字 -> 类名
形参列表:拷贝构造函数的第一个参数必须是本类对象的 const引用。
如果还需要其他的参数,那么必须要设置默认值(c++ 11新标准)
当没有实现拷贝构造函数的时候,编译器会自动生成一个,函数体内不是为空,而是执行逐个成员赋值
以自己简单实现的string类来举例:
class Mystring {
private:
char *m_ptr;
int m_size;
public:
/* 普通构造函数 */
Mystring(const char *m_ptr);
/* 拷贝构造函数 */
Mystring(const Mystring& other);
/* 移动构造函数 */
Mystring(Mystring&& other);
/* 析构函数 */
~Mystring();
void show() const;
int get_size() const;
};
/* 拷贝构造函数 */
Mystring::Mystring(const Mystring &other) {
m_ptr = new char[other.m_size + 1];
memcpy(m_ptr, other.m_ptr, other.m_size);
m_size = other.m_size;
#ifdef DEBUG
cout << "拷贝构造函数运行中" << endl;
#endif
}
思考:
拷贝构造函数的参数,为什么是本类对象的 const引用?
引用:提高效率。更重要的是如果不用引用传递,就会陷入无限拷贝中(实参传递给形参的过程又会调用拷贝构造函数)
const是为了不让原对象被修改
2、深拷贝和浅拷贝
Mystring::Mystring(const Mystring &other) {
//m_ptr = other.m_ptr; 浅拷贝
m_ptr = new char[other.m_size + 1]; //深拷贝
memcpy(m_ptr, other.m_ptr, other.m_size);
m_size = other.m_size;
}
分配新的堆区空间,而不是指向同一段堆区空间,这种为新对象分配资源的情况称之为深拷贝
拷贝构造函数被调用的具体时机
总的来说:用一个已有对象初始化一个新对象时会调用拷贝构造函数
1,直接初始化
Mystring str{"hello"};
Mystring str1{str};
//Mystring str1 = str;
str1.show();
cout << str1.get_size() << endl;
2, 一个对象作为参数,以值传递的方式传入函数内
void func(Mystring str) {
str.show();
}
Mystring str{"hello"};
func(str);
3, 一个函数返回class类型的对象
Mystring func() {
Mystring str{"hello world"};
return str;
}
Mystring str2 = func();
str2.show();
cout << str2.get_size() << endl;
二、移动构造函数
一个函数返回一个class对象时,本来会调用拷贝构造函数,但是经过编译器优化之后就没有调用拷贝构造函数了。
怎么优化的?? 不调用拷贝构造而是调用移动构造函数
如果程序员自己不实现移动构造函数,编译器就会默认生成一个移动构造函数
移动构造函数语法:
移动构造函数名(类名 && other)
{}
移动构造函数名:类名
&& 右值引用
Mystring::Mystring(Mystring &&other) {
m_ptr = other.m_ptr;
other.m_ptr = nullptr;
m_size = other.m_size;
#ifdef DEBUG
cout << "移动构造函数运行中" << endl;
#endif
}
移动构造函数的原理:
移动构造和拷贝构造:
对于新对象最终结果一样,实现过程不一样,提高了效率
对于原对象,拷贝构造之后还拥有资源,移动构造之后不再拥有资源
既有拷贝构造又有移动构造时,编译器如何选择:
用右值对象(临时对象/即将销毁的对象)去初始化 新对象时 ->调用移动构造
用左值对象去初始化新对象时 ->调用拷贝构造