//拷贝控制
有很多的容器或模板出现下面的应用
class Cla{}
Cla ca;
Cla cb;
ca(cb);
Cla cc = cb;
这两种应用就是使用了拷贝构造函数和拷贝赋值运算符。
拷贝构造函数是创建一个用来拷贝和自身一样类型的构造函数。
拷贝赋值运算符是使用运算符重载的功能,将=号重载成可以拷贝自身类型的函数。
//拷贝构造函数
//拷贝构造函数是将传来的自身类型参数中的元素依次拷贝到自身的对应元素中。
//拷贝初始化的限制,可以使用explicit来限制参数的类型转化。
class C_data {
public:
C_data();
//拷贝构造函数
C_data(const C_data& c_d) :x(c_d.x), p(c_d.p) {}
private:
int x;
string* p;
};
//拷贝赋值运算符
//使用重载运算符的功能创建函数,并传入与自身类型一样的参数
class C_data {
public:
C_data();
//拷贝赋值运算符
C_data& operator=(const C_data& c_d)
{
x = c_d.x;
p = c_d.p;
return *this;//返回一个自身的引用。
}
private:
int x;
string* p;
};
//析构函数
//析构函数是在对象被销毁时,自动调用的。
//析构函数体,是成员销毁步骤之外的一部分进行的,也就是会先执行析构函数体内的代码,再执行销毁。
class C_data {
public:
C_data();
//析构函数
~C_data() {}
};
//=default:可以显式的要求编辑器生成合成的拷贝控制成员
class C_data {
C_data() = default;
C_data(const C_data& c_d) = default;
C_data& operator=(const C_data& c_d) = default;
~C_data() = default;
};
//在类内使用=default时,合成的函数将隐式的声明为内联的。如果不希望合成的函数是内联的可以在类外定义使用=default。
//只能对具有合成版本的成员函数使用=default
//阻止拷贝,使用=delete,将合成的拷贝控制函数定义为删除的函数,就可以阻止拷贝。
//析构函数不能是删除的成员,如果删除的话就不能使用析构释放内存。
//如何判断类是否使用了阻止拷贝,本质上当不可能拷贝、赋值或销毁类的成员时,类的合成拷贝控制成员就被定义为删除的了。
//资源管理:大致分为两种,定义行为像值得类、行为像指针的类
//行为像值得类是拷贝对象的时候,原本和副本相互之间不受影响,两个是完全独立的。
//行为像指针就是副本和原本使用了相同的底层数据,是共享同一块内存的形式。
//行为像值得类的定义方式:
//创建拷贝赋值运算符函数,将新的参数中的元素,依次拷贝,并注意拷贝指针时要拷贝底层数据。
//根据三/五法则,在定义时还要加上拷贝构造函数和析构函数
class C_data {
public:
C_data();
C_data(const C_data& c_d) = default;
~C_data() = default;
//拷贝赋值运算符
C_data& operator=(const C_data& c_d)
{
auto newp = new string(*c_d.p);//拷贝底层的string,
delete p;//释放旧内存,必须要先拷贝再释放,避免拷贝的是同一个对象
auto nip = *c_d.ip;
delete ip;
ip = new int;
*ip = nip;
p = newp;
x = c_d.x;
return *this;//返回一个自身的引用。
}
private:
int x;
string* p;
int* ip;
};
//定义行为像指针的类
//使用引用计数器,当引用计数器为0时,每次拷贝时将引用计数加一,释放内存
//基本步骤是,先将被拷贝的对象引用计数加一,再判断引用计数,最后把所有数据拷贝过来,
class C_data {
public:
C_data();
C_data(const C_data& c_d) = default;
~C_data() = default;
//拷贝赋值运算符
C_data& operator=(const C_data& c_d)
{
++* c_d.use;//递增右侧对象的引用计数
if (-- * use == 0)//递减左侧对象的引用计数
{
delete p;
delete use;
}
p = c_d.p;//拷贝数据
x = c_d.x;
use = c_d.use;
return *this;//返回本对象。
}
private:
int x;
string* p;
std::size_t* use;
};
//交换操作
//创建wasp函数,并交换两个对象中的每个元素
class C_data {
friend void swap(C_data&, C_data&);
};
//定义成友元函数,便于访问类的private数据成员
inline void swap(C_data& c_d1, C_data& c_d2)
{
using std::swap;
swap(c_d1.p, c_d2.p);
swap(c_d1.x, c_da.x);
}