什么复制,看下面这个例子,来理解什么是复制
第一个例子
int main()
{int a = 2;int b = a;//实际是创建一个副本,他俩是独立的变量,它们有不同的内存地址(复制)b = 3;//是可以修改的std::cin.get();}第二个例子
struct Vector2
{float x, y;
};
int main()
{Vector2 a = { 2,3 };Vector2 b = a; //复制的是值,是吧a的值给了b,他们是两个不同的变量,占用的是两个不同的内存b.x = 5; //a中的x(a.x)还会是2std::cin.get();}第三个例子
struct Vector2
{float x, y;
};
int main()
{Vector2 *a = new Vector2();Vector2 *b = a; //这里我们看起来是复制了一些东西,但是我没有复制实际的向量,这个实际向量包含了x , y变量,我们复制的是指针b++; //影响指针,不会影响a中的x,yb->x = 2; //因为a b是指向同一个内存地址,所以这里会影响到astd::cin.get();}
为了更好的理解复制,我们自己写一个Strng类的例子:
接下来我们了解什么是构造函数:
首先,是一种特殊构造函数,如果没有显式的实现,编译器就会自动生成。
class 类名
{
public:// 拷贝构造类名(const 类名& that){}
};
为了更好的理解复制,我们自己写一个Strng类的例子:
#include <iostream>
#include <string>class String
{
private:char* m_Buffer; //字符缓冲区unsigned int m_Size; //字符的大小
public:String(const char* string){m_Size = strlen(string); //得到长度m_Buffer = new char[m_Size+1];//加一个空终止符memcpy(m_Buffer,string, m_Size+1); //des Src size}~String(){delete[] m_Buffer;}friend std::ostream& operator<<(std::ostream& stream, const String& string);//友元
};std::ostream& operator<<(std::ostream& stream, const String& string) //重载<<
{stream << string.m_Buffer;//需要成为友元之后才可以访问return stream;}
int main()
{String string = "dajian";String second = string;//复制std::cout << string << std::endl;std::cout << second << std::endl;std::cin.get();//会报错,为什么呢?
}
因为当我们复制这dajian是c++自动给我们做的是它将所有类的成员变量,将它复制到一个新的内存地址中,这个新的内存地址包含了second字符串,内存中有两个String(浅拷贝),他复制的是指针,这两个string中有相同的char* 的值,也就是这个m_Buffer的内存地址,对于这两个String对象来说是相同的(两个字符串指向完全相同的内存缓冲区),程序会奔溃,当我们执行完会,这两个String都被销毁了,就出现了douhle free的错误。
那我们应该怎么修改呢?那就是深拷贝
#include <iostream>
#include <string>class String
{
private:char* m_Buffer; //字符缓冲区unsigned int m_Size; //字符的大小
public:String(const char* string){m_Size = strlen(string); //得到长度m_Buffer = new char[m_Size+1];//加一个空终止符memcpy(m_Buffer,string, m_Size+1); //des Src size}//默认的拷贝构造/*String(const String& other): m_Buffer(other.m_Buffer),m_Size(other.m_size){}*///深拷贝String(const String& other) : m_Size(other.m_Size){m_Buffer = new char[m_Size + 1];memcpy(m_Buffer, other.m_Buffer, m_Size + 1);}~String(){delete[] m_Buffer;}friend std::ostream& operator<<(std::ostream& stream, const String& string);//友元
};std::ostream& operator<<(std::ostream& stream, const String& string) //重载<<
{stream << string.m_Buffer;//需要成为友元之后才可以访问return stream;}void PrintString(const String& string)
{std::cout << string << std::endl;
}
int main()
{String string = "dajian";String second = string;//复制std::cout << string << std::endl;std::cout << second << std::endl;std::cin.get();}
总结:
拷贝构造的任务是什么
拷贝构造参数对象的所有成员变量挨个赋值给新对象的成员变量,一般情况下编译器自动生成的拷贝构造就能完全满足我们使用需求。
什么时候需要显式实现拷贝构造
当成员变量中有指针成员且指向了堆内存,就需要显式实现拷贝构造。
编译器自动生成的拷贝构造,只会对成员变量挨个赋值,如果成员员中有指针变量且指向堆内存,结果就两个对象的指针变量同时指向一份堆内存,当它们执行析构函数时,会把这块堆内存释放两次,产生 double free or corruption 的错误。
正确的做法应该是先给新对象的指针变量重新申请一份堆内存,然后把旧对象的指针变量所指向的内存拷贝到新对象的指针变量所指向的内存。
浅拷贝与深拷贝
当类中的成员有指针变量且指向堆内存时,浅拷贝与深拷贝才有区别
浅拷贝 成员指针 = 成员指针。
深拷贝 memcpy(成员指针,成员指针)。
编译器自动生成的拷贝构造函数就是浅拷贝(把对象的成员进行挨个赋值),如果对象的成员有指针且指向堆内存,浅拷贝就会出现douhle free 的错误
深拷贝是批对象的成员中有指针且指针指向堆内存,不对指针成员赋值,而重新分配一块内存,把内存中的数据拷贝过来。