C++的复制与拷贝构造函数

拷贝,指的是要求复制数据,复制内存。

当我们想要把一个对象或原语或一段数据从一个地方复制到另一个地方时,实际上有两个副本。

当只是想读取的时候,或者要修改一个已经存在的对象的时候,应该避免不必要的复制。因为复制需要时间。

复制指针

变量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
相关推荐
第九序言T6 小时前
Python 数据处理进阶(十八)批量处理同结构文件:一个文件夹一次跑完
开发语言·python·自动化·pandas·数据处理
Sakura_j_h_6 小时前
EasyEDA PCB 快速设计Skills
c++·python·github
龙亘川7 小时前
构建城市公共安全数字底座:亘川智城智慧安防系统全维度解读
java·大数据·开发语言·人工智能·信息可视化
程序喵大人7 小时前
【C++入门】内存模型与资源管理 - 09 shared_ptr 和 weak_ptr 解决共享,也带来代价
开发语言·c++·资源管理·智能指针·内存模型
Raas1008 小时前
AI网关部署方案?从部署到上线全流程
java·开发语言·人工智能·gateway·ai网关·企业级ai网关·企业级ai产品
专注仿真8 小时前
线性回归和逻辑回归算法详解
开发语言·人工智能·python·大模型训练·控制算法
apple-mapping9 小时前
网络RTK定位学习总结
学习·网络rtk
jimy19 小时前
C++ 中的 `T&&`:右值引用与转发引用
开发语言·c++
呆呆虫129 小时前
用 orjson 加速 SerpBase 响应解析:基准对比与注意事项
开发语言·php
楚山之石10 小时前
【Lancet Neurology 】多发性硬化认知障碍的 SDMT 筛查、灰质萎缩标志与认知康复循证路径
图像处理·人工智能·学习·mri