C++中对象的复制通过一系列的构造函数或者赋值函数来实现的,包括copy construstor,move construstor,copy assignment operator,move assignment operator,实现过程中还可能有destructor的参与,一般情况下,这些函数编译器会为我们自动合成,但还有很多时候,编译器会将他们合成为delete,因此需要我们自己编写。也有一些时候,我们需要禁止或者强制编译器为我们合成这些函数。
首先看一下什么情况下编译器不会为我们自动合成这些函数
这些函数都有一个共同的特性,即析构函数被标记为delete或者不可访问时,所有编译器自动合成的函数都会被标记为delete,因为编译器不自愿生成无法析构的对象。同时,如果他们的父类或者成员的对应函数被标记为delete或者不可访问时,编译器也不会自动合成,因为编译器自动合成的函数需要依赖这些函数实现(内置类型不遵循这些规则)
default construstor:
1)一个类提供了自定义的任何构造函数
<span style="font-size:18px;">class Demon
{
public:
Demon(const string &s):id(s){}
private:
string id;
};
Demon d; ///错误,无默认构造函数</span>
2)一个类拥有标记为delete或者无访问权限的析构函数的成员或父类,或者其本身析构函数被标记为delete或无权访问
class Demon
{
~Demon()=delete;
};
class DerivedDemon:public Demon
{
};
class WrapDemon
{
private:
Demon d;
};
///下面三个对象创建都是错误的,因为
Demon d;
DerivedDemon dd;
WrapDemon wd;3)一个类拥有一个引用类型的成员,但并未对其进行类内初始化
<span style="font-size:18px;">class Demon
{
private:
int &r;
};
Demon d; ///错误,无默认构造函数</span>
4)一个类拥有一个无默认构造函数的父类,或者一个既无默认构造函数也未进行类内初始化的成员
<span style="font-size:18px;">class Mem
{
public:
Mem(const string addr):address(addr){}
private:
string address;
};
class Demon
{
private:
Mem m;
};
class DerivedDemon:public Demon
{
};
Demon d; ///错误,无默认构造函数
DerivedDemon dd; ///错误,无默认构造函数</span>
5)一个类拥有一个既无显式的默认构造函数,也未进行类内初始化的常量成员成员(如int常量)
<span style="font-size:18px;">class Demonx
{
private:
const int i;
};
class Demony
{
private:
const string s;
};
Demonx dx; ///错误,无默认构造函数
Demony dy; ///ok</span>
copy construstor:
1)一个类拥有标记为delete或者无访问权限的析构函数的成员或父类
2)一个类拥有标记为delete或者无访问权限的复制构造函数的成员或父类
3)一个类显示定义了move construstor或者move operator
<span style="font-size:18px;">class Demonx
{
public:
Demonx()=default;
Demonx(Demonx &&dx)=default;
};
Demonx dxa;
Demonx dxb = dxa; ///错误,copy construstor被标记为delete
Demonx dxc;
dxc = dxa; ///错误,copy assignment operator被标记为delete</span>
copy assignment:
1)一个类拥有标记为delete或者无访问权限的复制赋值运算符的成员或父类
2)一个类拥有一个常量成员或者一个引用成员
3)一个类显式定义了move construstor或者move operator
destrustor:
一个类拥有标记为delete或者无访问权限的析构函数的父类或者成员,当一个类拥有标记为delete或private的析构函数时,它的子类其实是无法实现析构函数的,只能将其标记为delete,因为子类的析构函数会自动调用父类析构函数的。同样,当一个类拥有标记为delete或者不可访问的析构函数的成员时,它本身的析构函数也无法实现
<span style="font-size:18px;">class Demon
{
~Demon=delete;
};
class DerivedDemon:public Demon
{
public:
~DerivedDemon()
{
///此析构函数会自动调用父类Demon的析构函数,
///因此错误
};
};
DerivedDemon dd; ///错误</span>
move construstor,move operator:
只有当一个类没有显式定义任何的复制构造函数,析构函数,复制赋值运算符,而且其所有成员和父类都可以移动,其析构函数可被访问,编译器词汇自动合成move construstor和move operator。(可移动是指一个成员要么是内置类型,要么提供了可访问的move construstor和move operator。常量成员和引用成员不可移动)
<span style="font-size:18px;">class Demonx
{
public:
~Demonx()=default;
};
class Demony
{
public:
Demony()=default;
Demony(const Demony&)=delete;
Demony& operator=(const Demony&)=delete;
};
Demonx dax;
Demonx dbx(move(dax)); ///ok,会调用copy construstor
///当move construstor或者move assignment operator被标记为delete时,
///需要使用它们时,会调用对应的copy construstor或者 copy assignment
Demony day;
Demony dby(move(day));
///错误,copy construstor被标记为delete,但已经属于显式定义,因此
///move construstor也被编译器自动标记为delete,导致调用出错</span>
当我们不需要编译器自动合成的某些函数时,可以将其标记为delete,但我们自己将其标记为delete时,除了不能调用该函数以外,属于显式定义的范畴,上面的例子已经有所说明。
class Demon
{
Demon(const Demon&)=delete;
};
Demon d; ///错误,显式定义了一个构造函数,因此默认构造函数被标记为deleteclass Demon
{
public:
Demon(Demon &&)=default;
Demon& operator=(const Demon &)=default;
virtual ~Demon()=default;
};
Demon d; ///错误,默认构造函数被标记为deleteclass Demon
{
public:
Demon()=default;
Demon(Demon &&)=default;
Demon& operator=(const Demon &)=default;
virtual ~Demon()=default;
};
Demon dx;
Demon dy = dx; ///错误,复制构造函数被标记为delete
class Demon
{
public:
Demon(const Demon&)=delete;
virtual ~Demon()=default;
};
class DerivedDemon:public Demon
{
public:
DerivedDemon()=default;
};
DerivedDemon da; ///错误,DerivedDemon依然被标记为delete综上,一般情况下,一个类如果拥有不可访问或者标记为delete的析构函数的成员或父类,那么它本身也无法实现析构函数,因为析构函数会自动调用父类和成员的析构函数,当一个类拥有不可访问或者被标记为delete的其他复制控制函数的成员或父类时时,编译器无法合成这些函数,即使我们强制合成,因为编译器合成这些函数的办法只有一个,及调用他们父类或以及成员的对应函数,但是我们还可以自己编写这些函数,但是也会有相当的不便。
总后给一个例子,说明上面这些函数的调用规则
class Demon
{
public:
Demon():id(++num)
{
cout<<"Demon "<<id<<" born"<<endl;
}
Demon(const Demon &d):id(++num)
{
cout<<"Demon "<<id<<" born by copy"<<endl;
}
Demon(Demon &&d):id(++num)
{
cout<<"Demon "<<id<<" born by move"<<endl;
}
Demon& operator=(const Demon &d)&
{
cout<<"Demon "<<id<<" value changed by copy"<<endl;
}
Demon& operator=(Demon &&)&
{
cout<<"Demon "<<id<<" value changed by move"<<endl;
}
virtual ~Demon()
{
cout<<"Demon "<<id<<" died"<<endl;
--num;
}
private:
size_t id;
static size_t num;
};
size_t Demon::num=0;
class DerivedDemon:public Demon
{
public:
DerivedDemon():d_id(++d_num)
{
cout<<"DerivedDemon "<<d_id<<" born"<<endl;
}
DerivedDemon(const DerivedDemon& dd):Demon(dd),d_id(++d_num)
{
cout<<"DerivedDemon "<<d_id<<" born by copy"<<endl;
}
DerivedDemon(DerivedDemon &&dd):Demon(move(dd)),d_id(++d_num)
{
cout<<"DerivedDemon "<<d_id<<" born by move"<<endl;
}
DerivedDemon& operator=(const DerivedDemon &dd)&
{
Demon::operator=(dd);
cout<<"DerivedDemon "<<d_id<<" value changed by copy"<<endl;
}
DerivedDemon& operator=(DerivedDemon &&dd)&
{
Demon::operator=(move(dd));
cout<<"DerivedDemon "<<d_id<<" value changed by move"<<endl;
}
~DerivedDemon()
{
cout<<"DerivedDemon "<<d_id<<" died"<<endl;
--d_num;
}
private:
size_t d_id;
static size_t d_num;
};
size_t DerivedDemon::d_num=0;
void test()
{
Demon *dpx = new DerivedDemon();
Demon *dpy = new DerivedDemon();
*dpx = *dpy;
*dpy = move(*dpx);
delete dpx;
delete dpy;
DerivedDemon da;
DerivedDemon db=da;
DerivedDemon dc(move(da));
DerivedDemon dd=move(db);
da = db;
dc = move(da);
}运行结果:
Demon 1 born
DerivedDemon 1 born
Demon 2 born
DerivedDemon 2 born
Demon 1 value changed by copy
Demon 2 value changed by move
DerivedDemon 1 died
Demon 1 died
DerivedDemon 2 died
Demon 2 died
Demon 1 born
DerivedDemon 1 born
Demon 2 born by copy
DerivedDemon 2 born by copy
Demon 3 born by move
DerivedDemon 3 born by move
Demon 4 born by move
DerivedDemon 4 born by move
Demon 1 value changed by copy
DerivedDemon 1 value changed by copy
Demon 3 value changed by move
DerivedDemon 3 value changed by move
DerivedDemon 4 died
Demon 4 died
DerivedDemon 3 died
Demon 3 died
DerivedDemon 2 died
Demon 2 died
DerivedDemon 1 died
Demon 1 died
原文地址:http://blog.csdn.net/yuanliang01xiaolang/article/details/41311703