拷贝、赋值、销毁
构造函数 ⇒ 对象初始化行为
赋值运算符 ⇒ 对象间赋值行为
析构函数 ⇒ 销毁行为
拷贝构造函数
copy constructor:第一个参数为自身(几乎都为const)的引用,额外参数都有默认值的构造函数
拷贝初始化
直接初始化=函数匹配
拷贝初始化=运算 (copy initialization) ⇒ 拷贝构造函数 or 移动构造函数
1."="定义变量
2.实参传递非引用形参
3.返回非引用对象
4.{list}初始化数组或聚合类
class mycl
{
public:
int a=0;
mycl(int i):a(i){cout<<"constructor!"<<endl;}
mycl(const mycl &c):a(c.a){cout<<"copy constructor!"<<endl;}
~mycl(){cout<<"destructor!"<<endl;}
};
mycl fun(const mycl &m)
{
cout<<"before copy"<<endl;
mycl mtmp(m);
cout<<"after copy"<<endl;
return mtmp; //这里返回mtmp到main时
}
int main()
{
int a=0;
mycl m1(a);
mycl m2=fun(m1);
return 0;
}
通常g++(大多数编译器)默认开启RVO(return value optimization),从而看不到构造临时返回变量的拷贝构造过程,可以通过"-fno-elide-constructors"命令关闭,还原拷贝构造返回值的过程
explict
explicit声明的函数只能直接初始化
class ClassA
{
public:
explicit ClassA(string s){}
//explicit只能声明在class内部 且只能作用于单变量构造函数
//此时无法完成ClassA a("123")中const char*到string的隐式转换
}
static_cast<ClassA>(s);
//可以显示强制转化使用string构造一个ClassA临时变量
拷贝构造函数在很多情况下是隐式使用的,因此不能是explicit的
不管有没有定义,编译器都会合成拷贝构造函数(synthesized copy constructor):会逐个拷贝数组
但有些情况并不希望某些对象能被拷贝(如istream类)
拷贝赋值运算符
copy-assignment operator:类未定义时编译器代为合成
部分运算符重载必须定义为成员函数(如“=”),左侧运算对象隐式绑定this,右侧显示传递参数
//等效合成拷贝赋值运算符
C& operator=(const C &r)
{
mem=r.mem; //将每个非static成员赋予左对象成员,操作我自己
return *this; //返回指向对象本身的引用,返回我自己
}
指向对象的引用或指针离开作用域时,其指向的对象并不会执行析构函数,需要delete
析构函数自身并不直接销毁成员,成员是在执行完析构函数体后的析构阶段被销毁的
三五法则
拷贝构造+拷贝赋值运算符+析构+移动构造+移动赋值
析构 ⇒(拷贝构造+赋值拷贝构造)= 成套定义
尤其注意:需要析构则一定要 拷贝构造函数 和 赋值拷贝运算符
class HasPtr
{
public:
string *p;
HasPtr(const string &s=string()):p(new string(s)){}
~HasPtr(){delete p;}
}
HasPtr fun(HasPtr h)
{
HasPtr r=h;
//使用了合成拷贝构造和赋值拷贝运算符,指针只是简单的被拷贝指向同一地址
return r;
//返回值后r和h均被销毁,但此时析构函数delete p两次,未定义的错位结果
}
阻止拷贝
如iostream类,定义多个对象将导致多个对象写入或读取相同的IO缓冲
通过"删除函数(deleted function)"来阻止拷贝
class C
{
public:
C()=default;
C(const C &)=delete;
//必须出现在第一次声明的时候=delete(预编译检查),防止被使用
C& operator=(const C &)=delete;
~C()=default;
//析构函数不能(无法通过编译)delete,否则无法销毁对象
}
当存在不能拷贝、赋值或销毁的成员时,则类的合成控制函数=delete
C11之前:通过将拷贝构造和赋值运算符声明为private但不定义实现阻止拷贝
class C
{
C(const C &);
C& operator=(const C &);
public:
C()=default;
~C()=default;
}
能用delete用delete!!! 能用delete用delete!!! 能用delete用delete!!!
移动构造函数
move constructor
移动赋值运算符
move-assignment operator
析构函数
destructor
对象移动