拷贝,指的是要求复制数据,复制内存。
当我们想要把一个对象或原语或一段数据从一个地方复制到另一个地方时,实际上有两个副本。
当只是想读取的时候,或者要修改一个已经存在的对象的时候,应该避免不必要的复制。因为复制需要时间。
复制指针
变量a,b本质上有相同的值。下面的操作,改变了内存地址对应的值。
cpp
#include <iostream>
#include <string>
struct Vector2
{
float x, y;
};
int main()
{
Vector2* a = new Vector2();
Vector2* b = a;
b->x = 2;
std::cin.get();
}
strlen()函数,得到string的大小。
cpp
#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]; // +1 表示增加了空终止符
//遍历每一个字符,然后一个一个复制。
//for (int i = 0; i < m_Size; i++)
// m_Buffer[i] = string[i];
//更加简洁的写法:使用memcpy()函数:目标、来源、大小。
//memcpy(m_Buffer, string, m_Size + 1);
//手动在最后添加空终止符:
memcpy(m_Buffer, string, m_Size);
m_Buffer[m_Size] = 0;
}
//将该运算符声明为友元
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 = "Miles";
std::cout << string << std::endl;
std::cin.get();
}
//输出:
//Miles
但在复制string的时候,程序崩溃了。
程序试图两次释放同一个内存块,而内存在第一次已经释放了。所以系统会奔溃。
代码如下:
cpp
#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]; // +1 表示增加了空终止符
memcpy(m_Buffer, string, m_Size);
m_Buffer[m_Size] = 0;
}
//需要手动删除
~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 = "Miles";
String second = string;
std::cout << string << std::endl;
std::cout << second << std::endl;
std::cin.get();
}
复制指针,两个字符串对象指向完全相同的内存缓冲区。
另一个例子,也会出现程序崩溃:
cpp
#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]; // +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];
}
//将该运算符声明为友元
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 = "Miles";
String second = string;
second[3] = 'a';
std::cout << string << std::endl;
std::cout << second << std::endl;
std::cin.get();
}
以上这些称作浅拷贝,不会去到指针的内容或者指针所指向的地方,也不会去复制它。
深度拷贝(深拷贝),实际上复制了整个对象。
拷贝构造函数
是一个构造函数,当复制第二个字符串的时候,它会被调用。当把一个字符串赋值给一个对象(字符串)时,
当试图创建一个新的变量并给它分配给另一个变量时,这个变量和正在创建的变量有相同的类型。若复制这个变量,这就是所谓的拷贝构造函数。
测试代码如下:
cpp
#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]; // +1 表示增加了空终止符
memcpy(m_Buffer, string, m_Size);
m_Buffer[m_Size] = 0;
}
String(const String& other)
: m_Buffer(other.m_Buffer), 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;
}
char& operator[](unsigned int index)
{
return m_Buffer[index];
}
//将该运算符声明为友元
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(String string)
{
std::cout << string << std::endl;
}
int main()
{
String string = "Miles";
String second = string;
second[3] = 'a';
PrintString(string);
PrintString(second);
std::cin.get();
}
//输出:
//Miles
//Milas