C++中浅复制及其存在的问题

news/2024/12/23 0:31:07/

C++中浅复制及其存在的问题

之前的示例程序中 MyString 类包含一个指针成员 buffer,它指向动态分配的内存(这些内存是在构造函数中使用 new 分配的,并在析构函数中使用 delete[]进行释放)。复制这个类的对象时,将复制其指针成员,但不复制指针指向的缓冲区,其结果是两个对象指向同一块动态分配的内存。销毁其中一个对象时, delete[]释放这个内存块,导致另一个对象存储的指针拷贝无效。这种复制被称为浅复制,会威胁程序的稳定性,如以下示例程序所示:

#include <iostream>
#include <string.h>
using namespace std;class MyString
{
private:char* buffer;public:MyString(const char* initString) // Constructor{buffer = NULL;if(initString != NULL){buffer = new char [strlen(initString) + 1];strcpy(buffer, initString);}}~MyString() // Destructor{cout << "Invoking destructor, clearing up" << endl;delete [] buffer;}int GetLength() { return strlen(buffer); }const char* GetString(){ return buffer; }
};void UseMyString(MyString str)
{cout << "String buffer in MyString is " << str.GetLength();cout << " characters long" << endl;cout << "buffer contains: " << str.GetString() << endl;return;
}int main()
{MyString sayHello("Hello from String Class");UseMyString(sayHello); return 0;
}

输出:

String buffer in MyString is 23 characters long
buffer contains: Hello from String Class
Invoking destructor, clearing up
Invoking destructor, clearing up
<crash as seen in Figure 9.2>

分析:

在之前的程序中运行正常的 MyString 类, 为何会导致现在这个程序崩溃呢?相比于以前的程序,现在这个程序唯一不同的地方在于,在 main( )中,将使用 MyString 对象 sayHello 的工作交给了函数UseMyString(),如第 44 行所示。在 main( )中将工作交给这个函数的结果是,对象 sayHello 被复制到形参 str,并在 UseMyString( )中使用它。编译器之所以进行复制,是因为函数 UseMyString( )的参数 str 被声明为按值(而不是按引用)传递。对于整型、字符和原始指针等 POD 数据,编译器执行二进制复制,因此 sayHello.buffer 包含的指针值被复制到 str 中,即 sayHello.buffer 和 str.buffer 指向同一个内存单元,如图 9.3 所示。
二进制复制不复制指向的内存单元,这导致两个 MyString 对象指向同一个内存单元。函数UseMyString( )返回时,变量 str 不再在作用域内,因此被销毁。为此,将调用 MyString 类的析构函数,而该析构函数使用 delete[]释放分配给 buffer 的内存(如程序清单 9.8 的第 22 行所示)。这将导致 main( ) 中的对象 sayHello 指向的内存无效,而等 main( )执行完毕时, sayHello 将不再在作用域内,进而被销
毁。但这次第 22 行对不再有效的内存地址调用 delete(销毁 str 时释放了该内存,导致它无效)。正是这种重复调用 delete 导致了程序崩溃。

该文章会更新,欢迎大家批评指正。

推荐一个零声学院的C++服务器开发课程,个人觉得老师讲得不错,
分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容
点击立即学习:C/C++后台高级服务器课程


http://www.ppmy.cn/news/1197668.html

相关文章

解决Visual Studio Code 控制台中文乱码问题

C和CPP运行编码指定 "code-runner.executorMap": {"c": "cd $dir && gcc -fexec-charsetGBK $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt","cpp": "cd $dir && g -fexec-charsetGBK $…

LVGL_多界面切换

LVGL_多界面切换 1、创建多个界面&#xff08;create_page1();&#xff09; 2、加载一个界面显示&#xff08;lv_scr_load(page1);&#xff09; 3、切换不同界面显示&#xff08;lv_scr_load_anim(page2, LV_SCR_LOAD_ANIM_OVER_LEFT, 300, 0, false);&#xff09; static lv_…

java的几种对象: PO,VO,DAO,BO,POJO

概述 对象释意使用备注PO&#xff08;persistant object&#xff09;持久对象可以看成是与数据库中的表相映射的Java对象&#xff0c;最简单的PO就是对应数据库中某个表中的一条记录。PO中应该不包含任何对数据库的操作VO&#xff08;view object&#xff09;表现层对象主要对…

SpringBoot-SpringCache缓存

文章目录 Spring Cache 介绍常用注解 Spring Cache 介绍 Spring Cache 是一个框架&#xff0c;实现了基于注解的缓存功能&#xff0c;只需要简单地加一个注解&#xff0c;就能实现缓存功能。 Spring Cache 提供了一层抽象&#xff0c;底层可以切换不同的缓存实现&#xff0c;…

Nginx 配置详细讲解

Nginx.conf 配置文件分为三部分&#xff0c;分别为main块、events块、http块&#xff08;http块又包含server块和location块&#xff09;&#xff0c;如下图。 第一部分&#xff1a;main块(全局块) main块主要是设置一些影响Nginx服务器整体运行的配置指令&#xff0c;主要包括…

c#局部类

partial是局部类型的意思。允许我们将一个类、结构或接口分成几个部分&#xff0c;分别实现在几个不同的.cs文件中。C#编译器在编译的时候仍会将各个部分的局部类型合并成一个完整的类 使用事项 关键字partial是一个上下文关键字&#xff0c;只有和 class、struct、interface…

逃逸分析:解锁性能的神秘钥匙!

优质博文&#xff1a;IT-BLOG-CN 面试管坑位&#xff1a;在Java中新创建的对象一定是在堆上分配内存吗&#xff1f;如果你的答案是“是的”那就需要看看这个文章了。 一、简介 逃逸分析Escape Analysis&#xff1a;是一个很重要的JIT优化技术&#xff0c;用于判断对象是否会…

Jtti:redis出现太多连接错误怎么解决

Redis出现太多连接错误通常是由于一些常见问题引起的&#xff0c;这些问题可能会导致连接超限、性能下降或服务不可用。以下是一些可能导致Redis连接错误的原因以及如何解决它们的建议&#xff1a; 1. 连接泄漏&#xff1a; 连接泄漏是指在使用完Redis连接后没有正确关闭它们。…