掌握C++:揭秘写时拷贝与浅深拷贝之间的关系

 更新时间:2024年01月30日 09:01:17   作者:字节连结  
探索C++的奥秘,本指南将揭秘写时拷贝与浅深拷贝之间的微妙关系,摸索这些复杂概念背后的逻辑,让你的编程技能瞬间提升,来吧,让我们一起进入这个引人入胜的C++世界!

1. 经典的string类问题

上一篇博客已经对string类进行了简单的介绍,大家只要能够正常使用即可。

在面试中,面试官总喜欢让学生自己来模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数。大家看下以下string类的实现是否有问题?

// 为了和标准库区分,此处使用String
class String
{
public:
	//String()
	//	:_str(new char[1])
	//{
	//	*_str = '\0';
	//}
	
	//String(const char* str = "\0")	// 错误示范
	//String(const char* str = nullptr)	// 错误示范
	String(const char* str = "")
	{
		// 构造String类对象时,如果传递nullptr指针,可以认为程序非法
		if (nullptr == str)
		{
			assert(false);
			return;
		}
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	
	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}
	
private:
	char* _str;
};

// 测试
void TestString()
{
	String s1("hello string");
	String s2(s1);
}

在这里插入图片描述

说明:上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题时,s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

2. 浅拷贝

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进行操作时,就会发生访问违规。

就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一不想分享就你争我夺,玩具损坏。


在这里插入图片描述

可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。父母给每个孩子都买一份玩具,各自玩各自的就不会有问题了。


在这里插入图片描述

3. 深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数,赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。
在这里插入图片描述

3.1 传统写法的String类

class String
{
public:
	String(const char* str = "")
	{
		// 构造String类对象时,如果传递nullptr指针,可以认为程序非法
		if (nullptr == str)
		{
			assert(false);
			return;
		}
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	
	String(const String& s)
		: _str(new char[strlen(s._str) + 1])
	{
		strcpy(_str, s._str);
	}
	
	String& operator=(const String& s)
	{
		if (this != &s)
		{
			char* pStr = new char[strlen(s._str) + 1];
			strcpy(pStr, s._str);
			delete[] _str;
			_str = pStr;
		}
		return *this;
	}
	
	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}
	
private:
	char* _str;
};

3.2 现代写法的String类

class String
{
public:
	String(const char* str = "")
	{
		if (nullptr == str)
		{
			assert(false);
			return;
		}
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	
	String(const String& s)
		: _str(nullptr)
	{
		String strTmp(s._str);
		swap(_str, strTmp._str);
	}
	
	// 对比下和上面的赋值那个实现比较好?
	String& operator=(String s)
	{
		swap(_str, s._str);
		return *this;
	}
	
	//String& operator=(const String& s)
	//{
	//	if (this != &s)
	//	{
	//		String strTmp(s);
	//		swap(_str, strTmp._str);
	//	}
	//	return *this;
	//}

	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}
	
private:
	char* _str;
};

传统写法就是老老实实自己开空间、自己拷贝数据、自己删除旧空间;而现代写法则利用了swap()函数以及局部变量出作用域自动销毁的特性,让函数和编译器帮我们“打工”,我们只要坐享其成即可。这两种方式在效率上并没有什么区别,只是让代码看起来更简洁,但这又会使代码的可读性降低。总体来说,我还是更偏向于传统写法。

4. 写时拷贝

在这里插入图片描述

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。

引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数-1,然后再检查是否需要释放资源,如果计数为1,说明该对象是资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象也在使用该资源。

到此这篇关于掌握C++:揭秘写时拷贝与浅深拷贝之间的关系的文章就介绍到这了,更多相关C++ 浅拷贝 深拷贝 写时拷贝内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++数据结构AVL树全面分析

    C++数据结构AVL树全面分析

    今天的这一篇博客,我要跟大家介绍一颗树——AVL树,它也是一颗二叉搜索树,它就是在二叉搜索树中加了一个平衡因子的概念在里面,下面我就来和大家聊一聊这棵树是个怎么样的树
    2021-10-10
  • 讲解C++编程中Address-of运算符&的作用及用法

    讲解C++编程中Address-of运算符&的作用及用法

    这篇文章主要介绍了C++编程中Address-of运算符&的作用及用法,是C++入门学习中的基础知识,需要的朋友可以参考下
    2016-01-01
  • VC使用TerminateProcess结束进程实例

    VC使用TerminateProcess结束进程实例

    这篇文章主要介绍了VC使用TerminateProcess结束进程的方法,实例演示了TerminateProcess结束进程的具体实现过程,在进行VC应用程序开发时非常具有实用价值,需要的朋友可以参考下
    2014-10-10
  • 在C语言中输入中文字符串讲解

    在C语言中输入中文字符串讲解

    这篇文章主要介绍了在C语言中输入中文字符串讲解,本文通过概念和案例相结合讲述了如何在C语言中使用中文,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • C语言编程入门必背的示例代码整理大全

    C语言编程入门必背的示例代码整理大全

    这篇文章主要为大家整理并介绍了C语言编程必背的示例代码大全,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2021-11-11
  • C++实现Dijkstra算法的示例代码

    C++实现Dijkstra算法的示例代码

    迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法。本文将用C++实现Dijkstra算法,需要的可以参考一下
    2022-07-07
  • C++中main函数怎样调用类内函数

    C++中main函数怎样调用类内函数

    这篇文章主要介绍了C++中main函数怎样调用类内函数问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • 从汇编看c++函数的默认参数的使用说明

    从汇编看c++函数的默认参数的使用说明

    本篇文章介绍了,在c++中函数的默认参数的使用说明分析。需要的朋友参考下
    2013-05-05
  • C++提取文件名与提取XML文件的方法详解

    C++提取文件名与提取XML文件的方法详解

    这篇文章主要为大家详细介绍了C++提取文件名与提取XML文件的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助<BR>
    2022-03-03
  • C++ 读取文件内容到指定类型的变量方法

    C++ 读取文件内容到指定类型的变量方法

    今天小编就为大家分享一篇C++ 读取文件内容到指定类型的变量方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07

最新评论