当前位置: 首页 > news >正文

C++对象的赋值与复制复制构造函数(指针数据成员)

一、对象的赋值

同类对象之间可以相互赋值,对象赋值的一般形式:
对象名2 = 对象名1;
原理是,赋值运算符的重载。仅赋值,因此赋值前,需要先定义并初始化对象2

对象的赋值针对指对象中所有数据成员的值
对象的赋值只对其中的数据成员赋值,不涉及成员函数。

#include <iostream>
#include <sstream>
using namespace std;class Student
{
public:Student(){num = 0;}Student(int n, string na, char s){num = n;name = na;sex = s;}void showinf( ){cout<<"num:"<<num<<"\tname:"<<name<<"\tsex:"<<sex<<endl;}     
private:int num;string name;char sex;
};int main()
{Student stud1(101,"LL",'m');//定义并初始化stud1,调用Student(int n, string na, char s);Student stud2;      //定义并初始化stud2,调用Student();stud1.showinf();stud2.showinf();stud2=stud1;        //赋值运算符重载,仅赋值操作,因此之前应先定义并初始化stud2stud2.showinf();return 0;
}

二、对象的复制

用已有的对象克隆出一个新对象,对象复制的一般形式为:
类名 对象2(对象1);   
原理:调用了编译系统提供的默认复制构造函数

Student∷Student(const Student& obj)    //默认构造函数
{num=obj.num;name=obj.name;sex=obj.sex;
}


既定义又复制,定义的同时执行复制对象操作!不需要提前定义和初始化对象2

等价形式:类名 对象2=对象1;(注:注意区分对象赋值操作)

#include <iostream>
#include <sstream>
using namespace std;class Student
{
public:Student(int n, string na, char s){num = n;name = na;sex = s;}void showinf( ){cout<<"num:"<<num<<"\tname:"<<name<<"\tsex:"<<sex<<endl;}     
private:int num;string name;char sex;
};int main()
{Student stud1(101,"LL",'m');Student stud2(stud1);         //等价于 Student stud2 =stud1;  既是定义,又是复制 == 定义&复制stud2.showinf();return 0;
}

三、复制构造函数

1、什么时候需要通过复制构造函数对对象进行复制?

(1)新建立一个对象时:直接利用复制构造函数进行定义和初始化Box box2(box1);
(2)当函数的参数为类的对象:调用函数时,将实参对象完整地传递给形参,通过调用复制构造函数来建立一个实参的拷贝;

void fun(Box b){ }int main( )
{Box box1(12,15,18);fun(box1);return 0;
}

(3)函数的返回值是类的对象:在函数调用完毕,将函数中的对象复制一个临时对象并传给该函数的调用处。

Box fun( )
{Box box1(12,15,18);return box1;
}
int main( )
{Box box2;box2=fun( );
}

2、复制构造函数的形式

复制构造函数的定义形式如下:

类名(const 类名&对象名)

Time(const Time & object);

3、浅复制

浅复制是指,使用默的复制方式{类名 对象2(对象1);或 类名 对象2 = 对象1}方式,复制数据成员的方式(此种方式无法传递指针数据成员)。

例1 当数据成员不包含指针时,此种复制方式不会出现问题。
#include <iostream>
using namespace std;
class Test
{
private:int x;
public:Test(int n) {x=n; }Test(const Test& obj){x=obj.x; }//复制构造函数void show (){cout<<x<<endl;}
};int main()
{Test test1(100);Test test2(test1); //对象复制形式一,调用复制构造函数Test(const Test& obj)Test test3=test1;  //对象复制形式二,调用复制构造函数Test(const Test& obj)test2.show();test3.show();return 0;
}

例2 当数据成员包含指针时,此复制方式会由于指针的传递出现异常
#include <iostream>
using namespace std;
class Test
{
private:int x;char *pcr;              //定义字符指针,可指向字符串或字符数组
public:Test(int n, char* c)    //初始化构造函数!!!{x=n;strcpy(pcr, c);     //错误点:这是一个野指针!!!!(但不是根源,根源是复制的方式不对)}Test(const Test& obj)   //复制构造函数!!!{x=obj.x;strcpy(pcr, obj.pcr);}void show (){cout<<"x:"<<x<<"\t pcr:"<<pcr<<endl;}
};int main()
{Test test1(100,'L');        //初始化对象test1Test test2(test1);          //对象复制形式一,调用复制构造函数Test(const Test& obj)Test test3=test1;           //对象复制形式二,调用复制构造函数Test(const Test& obj)test2.show();test3.show();return 0;
}

编译会报错。

4、深复制

深复制的目的:先给包含指针的数据成员分配空间,然后再去复制(避免了指针数据成员存在野指针情况)。

例3  在复制构造函数中预先为指针数据成员分配空间(在析构函数中释放空间)
#include <iostream>
using namespace std;
class Test
{
private:int x;char *pcr;               //定义字符指针,可指向字符串或字符数组       
public:Test(int n, char* c)     //初始化构造函数{x=n;int m = strlen(c)+1; //C语言中的字符串结尾\0,因此长度+1pcr = new char[m];   //为指针成员分配空间,避免野指针strcpy(pcr, c);     }Test(const Test& obj)   //复制构造函数{x=obj.x;int m = strlen(obj.pcr)+1;pcr = new char[m];          //为指针成员分配空间,避免野指针strcpy(pcr, obj.pcr);}~Test(){delete pcr;}            //由于构造函数中分配了空间,因此必须在析构函数中释放void show (){cout<<"x:"<<x<<"\t pcr:"<<pcr<<endl;}
};int main()
{Test test1(100,"Alibaba");  //初始化对象test1Test test2(test1);          //对象复制形式一,调用复制构造函数Test(const Test& obj)Test test3=test1;           //对象复制形式二,调用复制构造函数Test(const Test& obj)test2.show();test3.show();return 0;
}

例5 在复制构造函数中对指针数据成员直接传递【非常危险】
#include <iostream>
using namespace std;
class Test
{
private:int x;char *pcr;              //定义字符指针,可指向字符串或字符数组
public:Test(int n, char* c)     //初始化构造函数{x=n;pcr = c;            //!直接传递指针地址(貌似对,一定有机会错!)}Test(const Test& obj)   //复制构造函数{x=obj.x;pcr = obj.pcr;}void show (){cout<<"x:"<<x<<"\t pcr:"<<pcr<<endl;}
};int main()
{Test *pt1;pt1 = new Test(100,"GW");Test test2(*pt1);Test test3 = (*pt1);test2.show();test3.show();delete pt1;test2.show();test3.show();return 0;
}

注意:虽然通过传递指针的方式,实现了对象的复制。但是当Test *pt1被释放掉时,由pt1复制产生的test2和test3的数据成员中的指针数据成员,所指向的内存空间可能不变,也可能改变。
上图程序运行的结果是:释放后此段内存空间没变,但实际程序运行中则不一定。
所以,直接传递指针的方式非常危险!

http://www.lryc.cn/news/505930.html

相关文章:

  • Coding Caprice - monotonic stack2
  • Spring Mvc面试题(常见)
  • opencv # Sobel算子、Laplacian算子、Canny边缘检测、findContours、drawContours绘制轮廓、外接矩形
  • Neo4j插入数据逐级提升速度4倍又4倍
  • C++特殊类设计(单例模式等)
  • J8学习打卡笔记
  • 前端学习-操作元素内容(二十二)
  • 【踩坑】pip离线+在线在虚拟环境中安装指定版本cudnn攻略
  • golang操作sqlite3加速本地结构化数据查询
  • vllm加速(以Qwen2.5-7B-instruction为例)与流式响应
  • WordPress弹窗公告插件-ts小陈
  • 【ELK】容器化部署Elasticsearch1.14.3集群【亲测可用】
  • [SAP ABAP] ALV状态栏GUI STATUS的快速创建
  • 【Linux】NET9运行时移植到低版本GLIBC的Linux纯内核板卡上
  • 深入浅出支持向量机(SVM)
  • Vue脚手架相关记录
  • 基于Docker的Minio分布式集群实践
  • Scala 的迭代器
  • vue实现文件流形式的导出下载
  • 【DIY飞控板PX4移植】深入理解NuttX下PX4串口配置:ttyS设备编号与USARTUART对应关系解析
  • 【报错解决】vsvars32.bat 不是内部或外部命令,也不是可运行的程序或批处理文件
  • CTFshow-文件上传(Web151-170)
  • 深度学习基础--将yolov5的backbone模块用于目标识别会出现怎么效果呢??
  • 操作系统(16)I/O软件
  • leetcode437.路径总和III
  • WebGPU、WebGL 和 OpenGL/Vulkan对比分析
  • 不可重入锁与死锁
  • XXE-Lab靶场漏洞复现
  • 从Windows到Linux:跨平台数据库备份与还原
  • upload-labs