拷贝
除了引用以外,当一个变量赋值给另一个变量时,都是在拷贝。
可能是在拷贝指针(地址),也可能是在拷贝对象。
struct Vector2
{
float x,y;
};
int main()
{
// 拷贝对象
Vector2 v1 = {1,1};
Vector2 v2 = v1;
v2.x = 2;
cout<< v1.x<<endl; // 输出1,v1v2是不同对象
// 拷贝指针
Vector2* p1 = new Vector2();
Vector2* p2 = p1;
p2->x = 3;
cout<< p1->x<<endl; // 输出3,p1和p2操作的是同一对象
}
拷贝的缺点是慢,因此如果不需要新生成一样的变量的话应该避免拷贝。
浅拷贝
一种不完全的拷贝。所有成员的直接拷贝,但对指针来说,并不会拷贝内容,只是指针拷贝(两个指针指向相同地址)。这就是浅拷贝的缺点:这种情况下,通过一个指针改变内容,另一个指针的内容也会改变,一个指针删除,另一个指针会出错。但实际上我们希望对象拷贝的时候,新的指针会指向新的内存空间,并复制旧指针的内容。
如下自定义了一个字符串类,在main中进行字符串拷贝。
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);
m_Buffer[m_Size] = 0;
}
~String()
{
delete[] m_Buffer;
}
// 用于按索引取字符
char& operator[](unsigned int index)
{
return m_Buffer[index];
}
// cout能够直接输出private成员
friend ostream& operator<< (ostream& stream, const String& string);
};
ostream& operator<< (ostream& stream, const String& string)
{
stream << string.m_Buffer;
return stream;
}
int main()
{
String str1("chen");
String str2 = str1;
str2[2] = 'a';
cout<< str1<<endl;// chan
cout<< str2<<endl;// chan
}
这里你不光可以发现str2的修改直接影响了str1,你还得到了一个crush😘(对不起是crash)。原因是如果没有实现拷贝函数,c++默认拷贝方式是所有成员的赋值拷贝,这意味着char*只是拷贝了一个指针地址,导致str1和str2指向相同地址。这使得一个修改导致同时修改,并且crash是因为delete同一个char*两次。
深拷贝
不是表层的简单复制,还会为指针开辟新区域去复制。实现深拷贝,需要根据具体成员类型实现拷贝构造函数。
拷贝构造函数
在用拷贝方式创建新对象时调用。C++提供了默认的拷贝构造,但需要重写该函数。
对于上述例子,默认拷贝构造类似于:
String(const String& other)
:m_Buffer(other.m_Buffer), m_Size(other.m_Size){}
如果不允许拷贝,则用如下方式:
String(const String& other) = delete
深拷贝则写为:
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);
}
避免拷贝
如之前所说,拷贝会多占用内存,影响性能。在不需要拷贝的情况下尽量避免拷贝。
void Print(const String& str)
{
cout<< str<< endl;
}
正常情况下,对对象的修改都是由对象的成员函数完成的。类外函数不会修改对象,所以在对象为参数时,永远以const<type>&为参数类型传递,避免对象的复制。