《c++ primer》13.1章节
《c++程序设计语言 第四版》17.5.1章节
1、拷贝操作有两种:
- 拷贝构造函数:A(const A&)。初始化一片未经初始化的内存
- 拷贝赋值运算符:A& operator=(const A&)。处理的目标对象已构造且可能拥有资源的情况
2、拷贝初始化:定义变量时拷贝其他对象的值来给对象初始化,在以下情况发生:
- 用 = 定义变量时
- 将对象作为实参传递给非引用类型的形参时
- 从返回类型为非引用类型的函数返回对象时
- 用{}列表初始化数组中的元素或聚合类中的元素时
- 向容器中插入对象时(insert()和push()会拷贝对象)
3、拷贝构造函数必须使用引用的形式,原因是根据上面第2点,如果使用传值的方式,那么会触发拷贝初始化,即会调用拷贝构造函数,也就是当你要调用拷贝构造函数的时候的过程中会再次调用拷贝构造函数。这样无限套娃。
4、深拷贝和浅拷贝
深拷贝是指进行拷贝操作之后两个对象是相互独立的,浅拷贝相反,操作一个对象会改变另一个对象的状态:
#define debug qDebug()<<
class ceshi
{
public:
ceshi(int * p = nullptr)
{
this->p = p;
}
~ceshi()
{
if(p)
delete p;
}
ceshi(const ceshi &c)
{
this->p = c.p;
}
void debugValue()
{
if(p)
debug *p;
}
void changeValue(int value)
{
if(p)
*p = std::move(value);
}
private:
int * p{nullptr};
};
int main(int argc, char *argv[])
{
ceshi c1{new int(888)};
ceshi c2{c1};
c1.debugValue();
c2.debugValue();
c2.changeValue(444);
c1.debugValue();
c2.debugValue();
}
以上就是浅拷贝,拷贝时只拷贝指针的值,未拷贝指针指向的对象,当修改一个对象的状态时另一个的状态跟着变化。
这样的拷贝是深拷贝:
ceshi(const ceshi &c)
{
if(c.p)
{
this->p = new int(*c.p);
}
else
{
this->p = nullptr;
}
}
5、写前拷贝
写前拷贝就是拷贝使用浅拷贝,当要修改的时候再进行复制资源的操作:
#define debug qDebug()<<
class ceshi
{
public:
ceshi(int * p = nullptr)
{
this->p = p;
}
~ceshi()
{
if(p)
delete p;
}
ceshi(const ceshi &c)
{
this->p = c.p;
isShare = true;
}
void debugValue()
{
if(p)
debug *p;
}
void changeValue(int value)
{
if(isShare)
{
clone();
isShare = false;
}
if(p)
{
*p = std::move(value);
}
}
private:
int * p{nullptr};
bool isShare;//是否正在和其他对象共享资源
void clone()
{
if(isShare)
{
if(p)
{
p = new int(*p);
}
}
}
};
int main(int argc, char *argv[])
{
ceshi c1{new int(888)};
ceshi c2{c1};
c1.debugValue();
c2.debugValue();
c2.changeValue(444);
c1.debugValue();
c2.debugValue();
}
这里增加一个变量isShare,标识当前对象是否在和其他对象共享资源,拷贝后设为true,当改变值时调用clone()函数复制资源。
6、拷贝与切片
#define debug qDebug()<<
class A
{
public:
A(int a = 444)
{
this->a = a;
}
A(const A & obj)
{
this->a = obj.a;
debug "调用A拷贝";
}
private:
int a;
};
class B : public A
{
public:
B(int b = 555)
{
this->b = b;
}
B(const B & obj)
{
this->b = obj.b;
}
private:
int b;
};
int main(int argc, char *argv[])
{
B objB{888};
A objA = objB;
}
这里基类对象等于子类对象会调用基类的拷贝构造函数,得到的结果是一个基类对象,导致子类中的成员b丢失。这种现象操作切片,要禁止这类操作可以:
- 禁止基类拷贝:A(const A & obj) = delete;
- 不使用共有继承:class B : public A,改成private或protected