C++ 拷贝构造函数详解

article/2024/12/5 9:15:14

C++ 拷贝构造函数详解

下面的讲解将以C++标准库的string类作为讲解对象,string类:class with pointer member(s)

1、拷贝构造函数和拷贝赋值函数

1.1引入

下面是给出的测试函数,也是我们要能在自己设计的myString类中实现的功能:

int main()
{myString s1();	//无参数构造函数myString s2("Hello world!");	//传入字符串的构造函数myString s3(s1);	//拷贝构造cout<<s3<<endl;		//操作符重载,对<<重载s3 = s2;		//拷贝赋值cout<<s3<<endl;
}

当我们没有显式写出拷贝构造函数和拷贝赋值函数时,编译器会给我们默认提供拷贝构造和拷贝赋值函数,这两个函数做的都是逐字节地将一个对象的内容拷贝到另一个对象中。

对于成员没有指针的类,默认的拷贝构造函数一般不需要再重写。但是对于成员中含有指针的类,那么拷贝构造函数必须要进行重写。不能使用默认的拷贝构造函数。

#ifndef COPYCONSTRUCTOR_MYSTRING_H
#define COPYCONSTRUCTOR_MYSTRING_Hclass myString {
private:char* m_data;//动态分配的方式
public:myString(const char* cstr = 0);myString(const myString& str);//拷贝构造函数myString& operator=(const myString& str);//拷贝赋值~myString();//析构函数,类死亡的时候调用char* get_c_char()const{return m_data;};//inline function
};#endif //COPYCONSTRUCTOR_MYSTRING_H

下面我们先对普通的构造函数和析构函数进行创建:

inline
myString::myString(const char* cstr) {if(cstr){m_data = new char[strlen(cstr)+1];//别忘了结束符要占用长度strcpy(m_data, cstr);}else{//未指定初值m_data = new char[1];*m_data = '\0';}
}inline
myString::~myString() {delete[] m_data;
}

上述的创建分别使用了array new和array delete,即array[]和delete[]的写法。两者一定要搭配使用,不然会造成内存泄漏:

在这里插入图片描述

可以进行这样的测试:

{
myString s1();
myString s2("hello");myString* p = new myString("hello");
delete p;
}

使用new的关键字进行动态创建字符串,离开作用域时,必须写出delete p,删除指针对象。

未使用new关键字创建的字符串会自动调用析构函数。

1.2 拷贝构造函数

  • class with pointer members 必须有 copy cstr 和 copy op=

对于内含指针的构造函数,若使用默认的构造函数,则是**“浅拷贝”**memory leak,有可能造成内存泄漏。

**内存泄漏(Memory Leak)**是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

如下图,我们希望的是a和b各自有一个指针指向各自的字符串Hello\0,假使我们使用默认的拷贝构造,那么原先的World\0将不会有指针指向:造成了内存泄漏

alias:别名,两个指针指向同一个字符串也是非常危险的,a修改a的m_data,结果b的m_data也被修改了。这是我们所不希望的。

  • 拷贝构造函数的设置

    下面的写法就避免了内存泄漏,称为深拷贝

inline
myString::myString(const myString &str) {//创建出足够的空间放蓝本m_data = new char[strlen(str.m_data) + 1];//直接取另一个对象的private:兄弟之间互为友元strcpy(m_data, str.m_data);
}

可以看到在拷贝构造函数中,我们使用了另一个object对象的private成员,可以直接调用,这是因为同一个类的不同对象之间互为友元

可编写测试函数:

{myString s1("Hello");myString s2(s1);myString s3 = s1;//第三行和第四行是不同的操作,//一个是利用拷贝构造函数创建出一个新的对象//另一个是使用了拷贝赋值函数
}

1.3拷贝赋值函数

  • 赋值的过程
    • 销毁自己
    • 重分配空间
    • 返回*this
inline
myString & myString::operator=(const myString &str) {//检测自我赋值//self assignmentif(this == &str){//?return *this;}delete[] m_data;	//①m_data = new char[strlen(str.m_data)+1];//加上结束符的长度②strcpy(m_data, str.m_data);//③return *this;}

上述的①②③即分别对应上述赋值过程。特别要注意的是自我赋值,自我赋值的检测不仅关系到效率,还关系到下面程序的正确性。

2.new 和 delete

2.1对象的生命——堆空间和栈空间

对象一定存储在内存中,但可以是存储在栈空间,也可是在堆空间。

s1,s2,s3的内存空间在栈中,称为stack object,又叫做local object,因为其声明在作用域结束的时候就结束了,又被称为auto object,因为他被自动清理——析构函数被自动调用。

在这里插入图片描述

  • static变量
{myString s1 = myString();	//无参数构造函数static myString s2 = myString("Hello world!");	//传入字符串的构造函数return 0;
}

假如s2对象设定为static,那么这个statck object就会变成static对象,其生存期为程序的生存期,声明在作用域结束之后仍存在。

要注意的是static变量只会初始化一次。即重复调用函数修改static变量的值也只会修改一次。

  • 全局变量
class Complex{...};
...
Complex c3(1,2);int main()
{……
}

像c3这样的变量称为全局变量,其作用域和static变量一样。

2.2 new的正确使用方法

new 必须搭配delete使用,不然可能造成内存泄漏。

在这里插入图片描述

而new运算符会被分解成三个操作:

  • 分配内存:使用operator new函数,内部调用malloc,为对象分配内存
  • 转型:把void转型成为Complex
  • 构造函数:通过转型得到的指针调用其构造函数

在这里插入图片描述

即整个new的动作是:先分配内存,再调用构造函数

2.3 delete的使用

delete ps;编译器会将其转换为:

myString::~myString(ps);	//析构函数
operator delete(ps);	//释放内存

即delete被转化为两个动作:先调用析构函数然后释放内存。

而调用析构函数需要做什么?这需要我们自己定义:

myString::~myString() {delete[] m_data;
}

在我们定义的字符串的析构函数中,我们对动态申请的字符串的空间进行了删除,比如删除了字符串"hello word"。而字符串的m_data本身只是一个指针,此时还没有被删除。

operator delete(ps);则是内部调用free函数的一个函数,将指针删除。

而调用析构函数需要做什么?这需要我们自己定义:

myString::~myString() {delete[] m_data;
}

在我们定义的字符串的析构函数中,我们对动态申请的字符串的空间进行了删除,比如删除了字符串"hello word"。而字符串的m_data本身只是一个指针,此时还没有被删除。

operator delete(ps);则是内部调用free函数的一个函数,将指针删除。


http://chatgpt.dhexx.cn/article/oVepJYSg.shtml

相关文章

详解析构函数、拷贝构造函数

目录 一.析构函数&#xff08;析构器&#xff09; &#xff08;一&#xff09;.使用方式及注意事项 1.使用方式 2.注意事项 &#xff08;二&#xff09;.默认析构函数 二.拷贝构造函数 &#xff08;一&#xff09;.使用方式及注意事项 1.使用方式 2.注意事项 &#xff0…

【深入理解C++】拷贝构造函数

文章目录 1.拷贝构造函数2.默认的拷贝操作3.默认拷贝构造函数4.何时调用拷贝构造函数 1.拷贝构造函数 拷贝构造函数是构造函数的一种。当利用已存在的对象创建一个新对象时&#xff0c;就会调用新对象的拷贝构造函数进行初始化。 拷贝构造函数的格式是固定的&#xff0c;即接…

C++拷贝构造函数详解

一. 什么是拷贝构造函数 首先对于普通类型的对象来说&#xff0c;它们之间的复制是很简单的&#xff0c;例如&#xff1a; int a 100; int b a; 而类对象与普通对象不同&#xff0c;类对象内部结构一般较为复杂&#xff0c;存在各种成员变量。 下面看一个类对象拷贝的简…

c++拷贝构造函数(深拷贝,浅拷贝)详解

一、什么是拷贝构造函数 首先对于普通类型的对象来说,它们之间的复制是很简单的,例如: int a=100; int b=a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。 下面看一个类对象拷贝的简单例子。 #include<iostream> using n…

YOLO 裂缝检测

环境 python3.5 yolov3 opencv keras

基于OpenCV的混凝土裂纹检测

基于OpenCV的混凝土裂纹检测 前言 这是我发的第一次博客&#xff0c;有什么建议大家可以给我留言&#xff0c;感激不尽! 接下来&#xff0c;我们进入正题。 一、使用函数库 numpy, opencv, heapq, skimage.morphology 二、使用步骤 1.初步预处理 初步预处理包括&#xf…

【图像识别】基于计算机视觉实现路面裂缝检测识别系统matlab代码

1 简介 随着公路与铁路事业的飞速发展,各类车辆和里程的增加,铁路的一次次提速,都对路面产生了巨大的压力。不论是公路路面还是铁路路面,路面裂纹都能随处可见,由路面裂纹造成的交通事故时有发生。研究路面裂纹检测方法对于路面维护、交通安全具有极其重大意义。近年来,路面裂…

基于计算机视觉的裂纹检测方案

点击上方“小白学视觉”&#xff0c;选择加"星标"或“置顶” 重磅干货&#xff0c;第一时间送达01. 数据集 我们首先需要从互联网上获取包含墙壁裂缝的图像&#xff08;URL格式&#xff09;数据。总共包含1428张图像&#xff1a;其中一半是新的且未损坏的墙壁&#x…

halcon裂纹缺陷检测

针对这一类表面的检测就不能单纯依靠帧差或者背景差来完成&#xff0c;因为背景的纹理不可能和当前图像的纹理完全相同。 方法一—局部阈值分割 一、局部阈值分割 1、gen_sin_bandpass–局部阈值分割 dyn_threshold(OrigImage, ThresholdImage : RegionDynThresh : Offset,…

使用 Python 进行深度学习以进行裂纹检测

使用 Python 进行深度学习以进行裂纹检测 问题陈述数据集准备训练模型结论参考 问题陈述 虽然新技术已经改变了我们生活的方方面面&#xff0c;在建筑领域似乎牛逼正在努力追赶。目前&#xff0c;建筑物的结构状况仍然主要是人工检查。简单来说&#xff0c;即使现在需要检查结…

Halcon-表面检测-----裂纹检测

对应示例程序&#xff1a; detect_mura_defects_blur.hdev 目标&#xff1a;实例实现LCD上有很多污点干扰下&#xff0c;检测LCD的印痕检测。 思路为&#xff1a;对LCD图像进行拆分&#xff0c;提取RGB三个分量。 对B分量进行处理&#xff0c;将其转换为频域内图像&#xff0…

图像中的裂纹检测

01. 数据集 我们首先需要从互联网上获取包含墙壁裂缝的图像(URL格式)数据。总共包含1428张图像:其中一半是新的且未损坏的墙壁;其余部分显示了各种尺寸和类型的裂缝。 第一步:读取图像,并调整大小。 images = []for url in tqdm.tqdm(df[content]): response = req…

html图片与文字轮播,我是这样写文字轮播的

原标题&#xff1a;我是这样写文字轮播的 作者&#xff1a;一半水一半冰 原文&#xff1a;http://www.cnblogs.com/jingh/p/6377736.html 1写在前面 最近总结下之前的工作&#xff0c;才恍然发现时间的流逝永远是悄无声息的&#xff0c;离开学校那座象牙塔已经也有大半年的时间…

IOS 文本文字下面添加下划线

2019独角兽企业重金招聘Python工程师标准>>> UILabel *infolabel [[UILabel alloc] initWithFrame:CGRectMake(30*RATIO,260*RATIO, self.view.bounds.size.width-60*RATIO, 20*RATIO)];infolabel.text "infoimcba.com";infolabel.textColor [UIColor …

html语言 特效字,用HTML和CSS实现酷炫的文字特效

前言 马上我们就要进入下一个阶段&#xff0c;也就是HTML和CSS实现前端界面的阶段了&#xff0c;想必很多小伙伴都想给自己的页面加点酷炫的特效&#xff0c;今天&#xff0c;我就给大家整理了一些非常酷炫的文字特效来装点你的页面&#xff01;有些是从网络上找的&#xff0c;…

python抓取图片数字_python 实现识别图片上的数字

Python 3.6 版本 Pytesseract 图像验证码识别 环境: (1) win7 64位 (2) Idea (3) python 3.6 (4) pip install pillow <&nbsp>pip install pytesseract (5) 识别引擎tesseract-ocr 安装 安装tesseract-ocr的识别引擎 第一步:下载安装包 我下载的是64位,根据自…

html改变字母间距,css怎么调整字体间距?

在前端开发的过程中&#xff0c;有时候可能会出现字与字之间比较紧凑&#xff0c;我们可以通过css设置文字间隔让字与字之间的距离大一点&#xff0c;下面我们来看一下如何使用css调整字体间距。 css中解决字与字间隔的方法是使用css样式属性letter-spacing&#xff0c;letter-…

html ul 加点,HTML ul compact 属性 | 菜鸟教程

HTML compact 属性 实例 在无序列表中使用compact属性&#xff1a; CoffeeTeaMilk 尝试一下 浏览器支持 所有主流浏览器都不支持 compact 属性。 定义和用法 HTML5 不支持 compact 属性。 请使用 CSS 替代。 的 compact 属性在 HTML 4.01 已废弃。 compact 属性是一个 boole…

css怎么将文字底下设置颜色,css如何设置文字颜色

css设置文字颜色的方法&#xff1a;1、在DIV标签内使用color颜色样式&#xff0c;代码为【 www.php.cn 】&#xff1b;2、在CSS选择器中使用color颜色样式CSS代码。 本教程操作环境&#xff1a;windows7系统、css3版&#xff0c;DELL G3电脑。 css设置文字颜色的方法&#xff1…

markdown/LaTeX中在字母下方输入圆点的方法

看下面的关联代数的乘法定义, 如果需要在 z z z下面加上一个点, 应该怎么做呢? 下面是一个采用\stackrel命令的方法 {\huge{}_{\stackrel{{}_{{}_{{}_{{}_z}}}}{{}^\cdot}}}\!\leq y⋅ z ⁣ ≤ y {\huge{}_{\stackrel{{}_{{}_{{}_{{}_z}}}}{{}^\cdot}}}\!\leq y ⋅z​​​​​…