C语言日记 35 拷贝构造函数

news2025/7/18 8:19:25

书P132:

拷贝构造函数的作用是

用已存在的对象初始化另一对象,两对象类类型应一样

在这里我们可以看到,

他对被拷贝的对象的要求只有“已存在的对象,两对象类类型一样”,也就是说他这里也没有说我们不能跨区域(类)拷贝

那我们可不可以在不同的类中拷贝别的对象呢?(例如在A类里面拷贝B类的某一对象)

附:

提出这个问题的原因是因为这段话下面马上就有一句话“拷贝构造函数的形参是本类对象的引用”

那么我想他这么写是不是已经在明示暗示我们不可以在不同的类中拷贝别的对象了呢?

书P132拷贝构造函数举例:

类部分(类名和类体):

class Point
{
public:
	Point(int xx = 0, int yy = 0)
		//内联构造函数
	{
		X = xx; Y = yy;
	}
	Point(Point& p);//拷贝构造函数
	int GetX() { return X; }
	int GetY() { return Y; }
private:
	int X, Y;
}; 

类外(构造函数定义实现部分):

Point::Point(Point& p)
{
	X = p.X;
	Y = p.Y;
	cout << "拷贝构造函数被调用" << endl;
}

如果我们这里什么也不写,也就是说调用的是默认拷贝构造函数,除了在拷贝构造函数中不会写

cout的语句之外,其他的函数体和这里书上给出的函数体,一模一样

其中这里的cout语句和原内联构造函数不一样(原函数里面没有),其目的在于:

这样后面每次当我们的程序里面调用了拷贝构造函数时,每调用一次,我们就可以看到结果输出一次这个语句,而且我们还可以具体的看到具体的语句输出在哪里(什么时候被输出即拷贝构造函数在什么时候被调用)

合并并补充完整:

#include<iostream>
using namespace std;
class Point
{
public:
	Point(int xx = 0, int yy = 0)
		//内联构造函数
	{
		X = xx; Y = yy;
	}
	Point(Point& p);//拷贝构造函数
	int Getx() { return X; }
	int GetY() { return Y; }
private:
	int X, Y;
}; 
Point::Point(Point& p)
{
	X = p.X;
	Y = p.Y;
	cout << "拷贝构造函数被调用" << endl;
}
int main()
{

}

(1):(拷贝赋值,把对象A的值拷贝赋给对象B)

int main()
{
	Point A(6, 8);	//自动调用构造函数
	Point B(A);	//自动调用拷贝构造函数
	cout << B.GetX() << endl;
	return 0;
}

完整:(后面同理,不再赘述)

#include<iostream>
using namespace std;
class Point
{
public:
	Point(int xx = 0, int yy = 0)
		//内联构造函数
	{
		X = xx; Y = yy;
	}
	Point(Point& p);//拷贝构造函数
	int GetX() { return X; }
	int GetY() { return Y; }
private:
	int X, Y;
}; 
Point::Point(Point& p)
{
	X = p.X;
	Y = p.Y;
	cout << "拷贝构造函数被调用" << endl;
}
int main()
{
	Point A(6, 8);	//自动调用构造函数
	Point B(A);	//自动调用拷贝构造函数
	cout << B.GetX() << endl;
	return 0;
}

结果:

039be7c3b07b42bfb7081cecd81cc6db.png

另外,对于验证:

如果我们这里什么也不写,也就是说调用的是默认拷贝构造函数,除了在拷贝构造函数中不会写

cout的语句之外,其他的函数体和这里书上给出的函数体,一模一样

的说法的证明的程序如下:

#include<iostream>
using namespace std;
class Point
{
public:
	Point(int xx = 0, int yy = 0)
		//内联构造函数
	{
		X = xx; Y = yy;
	}
	int GetX() { return X; }
	int GetY() { return Y; }
private:
	int X, Y;
};

int main()
{
	Point A(6, 8);	//自动调用构造函数
	Point B(A);	//自动调用拷贝构造函数
	cout << B.GetX() << endl;
	return 0;
}

结果:

87ccd11ab64f4fdb9d0828b0c6d530b4.png

 后面也同理,不再赘述,只标注“使用默认拷贝构造函数结果”;

(2):(把实参A的值拷贝赋给形参p)

void fun1(Point p)//形参为类对象
{
	cout << p.GetX() << ',' << p.GetY() << endl;
}
int main()
{
	Point A(1, 2);
	fun1(A);
	return 0;
}

结果:

9e834481ed8d45f4a5e65e158d59fbfd.png

使用默认拷贝构造函数结果:

bab130006a6645808a200869368c5390.png


(3):返回值为类对象,系统自动把返回值拷贝到一个临时的无名对象中(详见书P133)

Point fun2()
{
	Point A(1, 2);
	return A;
}
int main()
{
	Point B;
	B = fun2();
	return 0;
}

结果:

8031f5117a21484da8e8e7a0daf34f24.png

使用默认拷贝构造函数结果:
4f68a0dcd6314ff498ca5a69ebd19033.png

例8-11 浅拷贝异常案例。

源程序:

#include<iostream>
using namespace std;
class Rect
{
private:
	int width;
	int height;
	int* p;//指针成员
public:
	Rect()//构造函数,p指向堆中分配的空间
	{
		p = new int(100);
		cout << "calling copy constructor!" << endl;
	}
	~Rect()	//析构函数,释放动态分配的空间
	{
		cout << "destructor is called!" << endl;
		delete p;
	}
};

int main()
{
	Rect rect1;
	Rect rect2(rect1);//复制对象
	return 0;
}

运行结果:

calling copy constructor!

destructor is called!

destructor is called!

539cd7a561b14d5fa724aee1bf7854f3.png

 3f81458258b64502b27a0e326d4ca3f0.png

像(在)这里开始,他才真正开始默认我们已经懂得和学过(默认)拷贝构造函数,不用在程序里面具体去写一遍默认拷贝构造函数的函数声明和函数体,全部用默认(隐式)拷贝构造函数;

而如果我们还没学过拷贝构造函数,由于前面构造函数的函数体看起来和往常根本没有任何区别,而后面他一下子就开始用起拷贝构造函数,不懂的话自然看着就懵了

另外,我们自己在判断程序的时候,不妨平时就把构造函数与拷贝构造函数捆绑在一起作为一个整体,简单的说,就是看到一个构造函数自动知道在其旁边就有着对应的拷贝构造函数

例8-12 深拷贝的使用。

源程序:

#include<iostream>
using namespace std;
class Rect
{
private:
	int width;
	int height;
	int* p;	//指针成员
public:
	Rect()//构造函数,p指向堆中分配的空间
	{
		p = new int(100);
		cout << "calling copy constructor!" << endl;
	}
	Rect(const Rect& r)
	{
		width = r.width;
		height = r.height;
		p = new int;//为新对象重新动态分配空间
		*p = *(r.p);
	}
	~Rect()//析构函数,释放动态分配的空间
	{
		cout << "destructor is called!" << endl;
		delete p;
	} 
};
//
int main()
{
	Rect rect1;
	Rect rect2(rect1);//复制对象
	return 0;
}

结果:

4f63bfac46944458961606eb9b1cfa4a.png

而这里这个程序和前一个示例的区别,(也就是深拷贝和浅拷贝的区别),(也)就是增加了一段给新的被粘贴的对象新写(创建)一个内存空间:

	Rect(const Rect& r)
	{
		width = r.width;
		height = r.height;
		p = new int;//为新对象重新动态分配空间
		*p = *(r.p);
	}

 其中const(我记得前面好像写过关于这个的解释,但是具体在哪里已经找不得了)的意思和具体作用,简单来说就是保证程序段只读不写(只能够读取,不能够修改)

详细情况和解释,见

69 结构体-结构体中const使用场景_哔哩哔哩_bilibili

另外,对于其在成员函数中的作用的补充,详见:35 类和对象-对象特性-const修饰成员函数_哔哩哔哩_bilibili

另外,如果要写得更简单简洁,也可以这样写:

	Rect(const Rect& r)
	{
		width = r.width;
		height = r.height;
		p = new int(*r.p);
		//p = (r.p);
		//编译器实际上默认实现的就是这个代码
	}

该段深拷贝浅拷贝的具体内容,详见:

27 类和对象-对象特性-深拷贝与浅拷贝_哔哩哔哩_bilibili

另外,为了巩固这块内容,(实际上是因为我们想达到一个深拷贝以后还能把两个对象里面的具体数据输出的效果)我们这里再把程序补充完整为处处对象具体内容同的程序:

#include<iostream>
using namespace std;
class Rect
{
private:
	int width;
	int height;
	int* p;	//指针成员
public:
	Rect()//构造函数,p指向堆中分配的空间
	{
		p = new int(100);
	}
	Rect(const Rect& r)
	{	
		cout << "calling copy constructor!" << endl;
		width = r.width;
		height = r.height;
		p = new int(*r.p);
		//p = (r.p);
		//编译器实际上默认实现的就是这个代码
	}

	~Rect()//析构函数,释放动态分配的空间
	{
		cout << "destructor is called!" << endl;
		delete p;
	}
	int display()
	{
		cout << "指针指向" << *p << 
			";   高度" << height << ";   宽度" << width << endl;
		return 1;
	}
};
//
int main()
{
	Rect rect1;
	Rect rect2(rect1);//复制对象
	cout << rect1.display() << endl;
	cout << rect2.display() << endl;
	return 0;
}

结果:

 改动之处:

  • 把显示输出调用拷贝构造函数的句子放到了拷贝构造函数中,而不是构造函数里面
  • 新写了display()函数,用于输出这几个对象里面的具体内容

只得一提的是:

我们原来一开始本来打算写两种形式的display()函数:

一种采用cout语句输出值,另一种采用return语句返回值

然而,当我们一开始只写cout语句的display()函数时:

	int display()
	{
		cout << "指针指向" << *p << 
			";   高度" << height << ";   宽度" << width << endl;
	}

结果:

所以在这里我们可以知道:

在类中定义的函数必须有一个返回值

但是总感觉这好像又不对吧,例如:C语言日记 32 类的对象,this指针_宇 -Yu的博客-CSDN博客

的 例8-6,这里面的

void Time::Get_Time()//定义公有成员函数
{
	cout << Hour << ":" << Minute << ":" << Second << endl;

}

好像也没有写返回的值啊??

另外,如果我们采用return语句返回值,则display()函数为:

(1):

	int display()
	{
		return   *p ;
		return height;
		return width;
	}

结果:

 (2):

	int display()
	{
		return height;
		return   *p ;
		return width;
	}

结果:

(所以)那么这里我们就可以说,

当我们有多个返回值,return语句的时候,无论我们有多少个返回值,最终只输出第一个返回值(输出只输出第一个返回值,返回是不是只有第一个返回值(那)我就不知道了)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/8190.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用 Spring Cloud Loadbalancer 实现客户端负载均衡

使用 Spring Cloud Loadbalancer 实现客户端负载均衡 作者&#xff1a;Grey 原文地址&#xff1a; 博客园&#xff1a;使用 Spring Cloud Loadbalancer 实现客户端负载均衡 CSDN&#xff1a;使用 Spring Cloud Loadbalancer 实现客户端负载均衡 背景 在Spring Cloud G 版…

为什么 think-cell 图表中的标签显示为白色矩形?

有些标签的背景错误地变成白色&#xff0c;或显示幻灯片背景。当前版本的 PowerPoint 不会出现此问题。 •使用 PowerPoint 2007 打开 .ppt 文件并单击标签内部时&#xff0c;将显示正确的标签背景&#xff0c;但是在保存后重新打开该文件时&#xff0c;会返回不正确…

分布式系统的 38 个知识点

天天说分布式分布式&#xff0c;那么我们是否知道什么是分布式&#xff0c;分布式会遇到什么问题&#xff0c;有哪些理论支撑&#xff0c;有哪些经典的应对方案&#xff0c;业界是如何设计并保证分布式系统的高可用呢&#xff1f; 1. 架构设计 这一节将从一些经典的开源系统架…

5G无线技术基础自学系列 | 物理随机接入信道

素材来源&#xff1a;《5G无线网络优化实践》 一边学习一边整理内容&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 PRACH信道用于传输前导Preamble序列。gNB…

逆向-破零64位版本

// 排版有点乱&#xff0c;没有图片直观。 #include <stdio.h> #include <stdlib.h> #include <string.h> int f_14a2(int *va, int vb) { /*14a2: f3 0f 1e fa endbr64 14a6: 55 push %rbp 14a7:…

[附源码]java毕业设计基于Web的美食网站的设计与实现

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【背景渐变】 —— 就算没接触过也能 一遍学会哦

前期回顾 ​回顾 前期 把你喜欢css动画嵌入到浏览器中 —— css动画 项目_0.活在风浪里的博客-CSDN博客常用酷炫动画999合集&#xff0c;代码直接复制可用&#xff0c;总用你想找的&#xff0c;快来抱走吧&#xff0c;三连&#xff0c;停&#xff01;听鹅说&#xff0c;下…

2009年408大题总结

2009年408大题第41题第42题第43题第44题第45题第46题第47题第41题 这个最容易想到的方法就是举反例&#xff0c;但是我们可以分析一下&#xff0c;每一次都取最短的路径&#xff0c;实际上就是贪心策略的应用——每次都是最优&#xff0c;但是最终的结果却一般不是最优&#xf…

第十一节:抽象类和接口【java】

目录 &#x1f947;1. 抽象类 &#x1f4d8;1.1 抽象类概念 &#x1f4d2;1.2 抽象类语法 &#x1f4d7; 1.3 抽象类特性 &#x1f3f9;2. 接口 &#x1f4d9;2.1 接口的概念 &#x1f4d5;2.2 语法规则 &#x1f4d2;2.3 接口使用 &#x1f50e;2.3.1USB接口例子 &a…

【Google Colab】使用手册、教程;使用 Google Colab 免费使用 python 服务器

Colaboratory 是一个 Google 研究项目&#xff0c;旨在帮助传播机器学习培训和研究成果。它是一个 Jupyter 笔记本环境&#xff0c;不需要进行任何设置就可以使用&#xff0c;并且完全在云端运行。Colaboratory 笔记本存储在 Google 云端硬盘中&#xff0c;并且可以共享&#x…

超详细图解kafka生产消费流程

目录&#xff1a; 一条消息在kafka是如何生产的&#xff1f; 一条消息在kafka是如何存储的&#xff1f; 一条消息在kafka是如何消费的&#xff1f; Kafka为什么高性能&#xff1f; Kafka为什么高可用&#xff1f; 一&#xff0e;一条消息在kafka是如何存储的&#xff1f; …

【前端】Vue+Element UI案例:通用后台管理系统-登陆不同用户显示不同菜单、动态添加路由

文章目录目标代码0.动态地显示菜单&#xff1a;store1.动态注册路由2.解决刷新后摆平问题总代码本篇修改的代码文件tab.js参考视频&#xff1a; VUE项目&#xff0c;VUE项目实战&#xff0c;vue后台管理系统&#xff0c;前端面试&#xff0c;前端面试项目案例链接【前端】VueEl…

spring-IOC理论推导P3

1.新建一个项目。名字叫做spring-study 2.导包 <dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.23</version></dependency></dependencies&…

solr-7.7.3 搭建

solr-7.7.3 搭建 solr-7.7.3 搭建 文章目录solr-7.7.3 搭建一、前期准备安裝规划(一)资料下载(二)上传文件二、安裝(一)创建新用户(二)安装solr1.solr安装包解压到/opt/module2.将解压文件名修改为solr3.修改配置文件(三)分发文件&#xff08;四&#xff09;开启solr&#xff…

YOLOv5量化调优

目录 一、背景 二、模型调优 2.1 基准选取 2.1.1 官方精度数据 2.1.2 fp32bmodel的精度 2.1.3 int8bmodel精度数据 2.2 多图量化 2.3 预处理对齐&lmdb 2.4 网络图优化 2.4.1 per_channel优化 2.4.2 accuracy_opt优化 2.4.3 conv_group优化 2.4.4 总结 2.5 混…

python中使用xlrd、xlwt操作excel表格详解

最近遇到一个情景&#xff0c;就是定期生成并发送服务器使用情况报表&#xff0c;按照不同维度统计&#xff0c;涉及python对excel的操作&#xff0c;上网搜罗了一番&#xff0c;大多大同小异&#xff0c;而且不太能满足需求&#xff0c;不过经过一番对源码的"研究"&…

移动电源出口美国和欧盟需要做什么?

手机的普遍使用也带动了充电宝的发展&#xff0c;现在是智能的时代&#xff0c;手机现在是我们每个人永远不会忘记带的东西&#xff0c;当然耗电量也是很大的。所以充电宝在这时就有很大的用处了。移动电源是一种集供电和充电功能于一体的便携式充电器&#xff0c;一般由锂离子…

杰理的蓝牙芯片的key是什么?以及该如何添加key?杰理key文件原理

目录 一、简介 关于杰理芯片的key文件&#xff0c;实际上 杰理芯片特有的一种机制&#xff0c;而这种机制就是存在于杰理芯片特有的架构&#xff0c;也是杰理公司延续将近10年的特点&#xff0c;估计以后也会是这种机制。具体为什么&#xff0c;请听我娓娓道来&#xff0c;这里…

测试大咖漫谈如何搞定软件质量?

关于质量保障&#xff0c;好像已经说过太多&#xff0c;但这里还是抽象的唠叨几句。 多年的软件测试和质量保障工作让我越来越清晰的认识到&#xff1a;质量绝对不是一个环节&#xff0c;一个工种可以搞定的。比如&#xff1a; 从对语言的误用&#xff0c;到对第三方组件的误用…

第4关: 网页排序——PageRank算法

要求&#xff1a;编写实现网页数据集PageRank算法的程序&#xff0c;对网页数据集进行处理得到网页权重排序。 ####相关知识 ######PageRank算法原理 1.基本思想&#xff1a; 如果网页T存在一个指向网页A的连接&#xff0c;则表明T的所有者认为A比较重要&#xff0c;从而把T的一…