C++深浅拷贝和写时拷贝图文详解
作者:_Camille 发布时间:2021-06-07 00:12:58
标签:c++,深浅拷贝,写时拷贝
前言
之前我们在浅谈6个成员函数中有提到深浅拷贝的问题,现在再回首掏一把。
一、深浅拷贝哪家强?
先给出代码理一理
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include<assert.h>
using namespace std;
class String
{
friend ostream& operator<<(ostream &out, const String &s);
public:
String(const char* str = "")
{
m_data = new char[strlen(str) + 1];
strcpy(m_data, str);
}
//String(const String& s)//qian拷贝
//{
//m_data = s.m_data;
//}
String(const String& s)//深拷贝
{
m_data = new char[strlen(s.m_data) + 1];
strcpy(m_data, s.m_data);
}
String& operator=(const String& s)
{
if (this != &s)
{
delete[]m_data;
m_data = new char[strlen(s.m_data) + 1];
strcpy(m_data, s.m_data);
}
return *this;
}
~String()
{
delete[]m_data;
m_data = nullptr;
}
private:
char* m_data;
};
ostream& operator<<(ostream &out, const String &s)
{
out << s.m_data;
return out;
}
void main()
{
String s1("abc");
String s2 = s1;
cout << "s1 = " << s1 << endl;
cout << "s2 = " << s2 << endl;
}
而我们之前所说的浅拷贝崩溃是因为doublefree的问题,因此我们可以定义一个引用计数器,来记录当前使用该值的对象数,如果数目大于1,则不释放内存。
class String
{
friend ostream& operator<<(ostream &out, const String &s);
public:
String(const char* str = "")
{
m_data = new char[strlen(str) + 1];
strcpy(m_data, str);
m_count++;
}
String(const String& s)//浅拷贝
{
m_data = s.m_data;
m_count++;
}
String& operator=(const String& s)
{
if (this != &s)
{
m_data = s.m_data;
m_count++;
}
return *this;
}
~String()//浅赋值
{
if (--m_count == 0)
{
delete[]m_data;
m_data = nullptr;
}
}
private:
char* m_data;
static int m_count;//引用计数器
};
int String::m_count = 0;
ostream& operator<<(ostream &out, const String &s)
{
out << s.m_data;
return out;
}
void main()
{
String s1("abc");
String s2 = s1;
String s3;
s3 = s2;
cout << "s1 = " << s1 << endl;
cout << "s2 = " << s2 << endl;
cout << "s3 = " << s3 << endl;
}
可以看出,三个对象的m_data共享同一块内存空间,节省了资源;
但是暴露出了很多的问题:站在对象的角度,其中一个对象改变m_data其他的对象也会随之改变;其二若s3使用其他字符串初始化,但计数器还是三者共享。
倘若我们使用深拷贝方法,就不会出现这种问题。如果可以在不改变m_data前使用浅拷贝,在改变时使用深拷贝,暨同时实现深浅拷贝,那么就两全其美。
二、写时拷贝
通过对上面问题的分析,我们需要实现:引用计数器管理不同的空间。
class String_rep
{
public:
String_rep(const char* str = "") :m_count(0)
{
m_data = new char[strlen(str) + 1];
strcpy(m_data, str);
cout << "creat" << endl;
}
String_rep(const String_rep &rep) :m_count(0)
{
m_data = rep.m_data;
increment();
}
String_rep & operator=(const String_rep &rep)
{
if (this != &rep)
{
m_data = rep.m_data;
increment();
}
return *this;
}
public:
void increment()
{m_count++;}
void decrement()
{m_count--;}
private:
char* m_data;
int m_count;
};
class String
{
public:
String(const char* str = "") :pn(new String_rep(str))
{
pn->increment();
}
~String()
{
cout << "Free" << endl;
}
private:
String_rep *pn;
};
void main()
{
String s1("abc");
}
拷贝构造:s1和s2管理同一块空间
定义s3,和s1、s2没有关联;
我们再完全理一遍:
此时已经解决我们之前提到过的第二个问题。
再来看第一个问题:
s1的改变影响了s2;
写时拷贝:需要改变的时候深拷贝。
void to_upper()
{
String_rep *new_pn = new String_rep(pn->Getdata());//创建新空间
pn->decrement();//原空间计数器减一
pn = new_pn;//需要更改的对象的pn指向新空间
pn->increment();//新空间的计数器加一
char* p = pn->Getdata();
while (*p != '\0')
{
if (*p >= 'a' && *p <= 'z')
*p -= 32;
p++;
}
总结
来源:https://blog.csdn.net/qq_43560037/article/details/115425535


猜你喜欢
- 经测试,是环绕通知改变了返回值,切面方法需要有返回值,来代替被代理方法返回结果改成如下即可:@Around("point_upda
- 简介本文用示例介绍SpringBoot如何解决BigDecimal传到前端后精度丢失问题。问题描述实例Controllerpackage c
- Spring相关的依赖导入进去,即可使用spring的定时任务!<!-- spring核心包 -->
- 1.前言NameServer主要作用是为消息消费者和消息生产者提供关于主题Topic的路由信息,那么NameServer需要存储路由的基本信
- 本文实例为大家分享了java客户端登陆服务器用户名验证的具体实现代码,供大家参考,具体内容如下客户端通过键盘录入用户名,服务端对用户名进行验
- MyBatis-Plus (简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效
- 前言:文件的上传和下载在日常开发中很是常见,那么这一功能是如何实现的呢,下面我给大家介绍一下实现条件:1、需要一个form标签,method
- c# 轮询算法这两天做东西,业务上有个特殊的需求,在用户访问页面的时候,针对某一行代码进行控制,按照概率来进行显示,我做的是针对当前页面的曝
- InetAddress类InetAddress类用来封装我们前面讨论的数字式的IP地址和该地址的域名。你通过一个IP主机名与这个类发生作用,
- 1、前言 最近做项目需要用到监测网速及流量,我经过百度和墙内谷歌都没能快速发现监测IPV6流量和网速的用例;也经过自己的一番查询和调试,浪
- 一、作品展示1、菜单界面(注:由于特殊原因,原图无法展示,请谅解)2、答题界面(注:由于特殊原因,原图无法展示,请谅解)3、学习模式界面(注
- Sentinel数据双向同步上面实现了Nacos单向同步配置规则到Sentinel,但是只是单向的,没有实现Sentinel向Nacos同步
- Java是一种面向对象的编程语言,由Sun Microsystems公司在1995年的时候正式发布。直到今天,Java都一直是最受欢迎的编程
- 一、User Agent的含义User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及
- Java5以后开始支持枚举类型,枚举类型使用起来非常方便,其重要的作用是作为类型安全使用的。如果在不考虑系统内存开销的情况下大量的使用枚举也
- 本博文将为您提供自Java 7以来增加的很棒的新功能的示例。我将展示每个Java版本的至少一项重大改进,一直到2020年秋季发布的Java
- 今天在做一个联系人管理的C#设计时,遇到了这个问题,我需要将父窗体中的textBox中的值传到子窗体并进行数据库查询操作,我用了new 父窗
- 本文实例讲述了C#通过流写入一行数据到文件的方法。分享给大家供大家参考。具体如下:using System;using System.IO;
- 前言WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏
- 重复参数 Scala在定义函数时允许指定最后一个参数可以重复(变长参数),从而允许函数调用者使用变长参数列表来调用该函数,Scala中使用“