C++拷贝构造函数原理解析

news2024/10/7 4:34:41

喵~

  • 一、构造函数
    • 1.1 默认构造函数
    • 1.2 自定义的默认构造函数
    • 1.3 自定义带参数的构造函数
  • 二、拷贝构造函数的基本使用
    • 2.1 浅拷贝和深拷贝(原理及区别)

一、构造函数

在C++面向对象的学习中,对于构造函数应该并不陌生,有默认的构造函数,也有自定义的构造函数。假设有下面类的框架

1.1 默认构造函数

class Human{
public:
	void eat();
private:
	//私有变量,必须提供共有get方法才能获取
	string name;
	int age;
};

那么此时打印的时候,因为没有在类中定义构造函数,编译器会自动调用默认构造函数,此构造函数没有参数列表没有函数体Human(){};是一个空的构造函数。如果没有自定义构造函数的话编译器就自动调用该构造函数,如果一旦在程序中自定义了构造函数,默认的构造函数及时作废,也就不会再被调用了

1.2 自定义的默认构造函数

class Human{
public:
	Human();
	void eat();
	string getName();
	int getAge();
private:
	//私有变量,必须提供共有get方法才能获取
	string name;
	int age;
};
string Human::getName(){
	return name;
}
int Human::getAge(){
	return age;
}
void Human::eat(){
	cout << "eat" << endl;
}
Human::Human(){
	name = "zhongguo";
	age = 9000;
}

int main(){
	Human hu;
	cout << hu.getName() << endl;//zhongguo
	cout << hu.getAge() <<endl;//9000
	return 0;
}

自定义了构造函数,但是并没有但参数,一般将这种称之为自定义的默认构造函数,当然称为自定义的构造函数也没有什么错。上面的写法其实也可以合二为一,就是将构造函数Human()的原型和定义放在一起,放在public下面。

class Human{
public:
	Human(){
		name = "zhongguo";
		age = 9000;
	}
	void eat();
	string getName();
	int getAge();
private:
	//私有变量,必须提供共有get方法才能获取
	string name;
	int age;
};

1.3 自定义带参数的构造函数

这种方式是最为常见的构造函数的写法,

class Human{
public:
	Human(string name,int age);
	void eat();
	string getName();
	int getAge();
private:
	//私有变量,必须提供共有get方法才能获取
	string name;
	int age;
};
string Human::getName(){
	return name;
}
int Human::getAge(){
	return age;
}
void Human::eat(){
	cout << "eat" << endl;
}
Human::Human(string name,int age){
	this->name = name;
	this->age = age;
}

int main(){
	Human hu("zhongguo",9000);
	cout << hu.getName() << endl;//zhongguo
	cout << hu.getAge() <<endl;//9000
	return 0;
}

二、拷贝构造函数的基本使用

同样我们在程序中没有定义拷贝构造函数的时候,编译器调用的是默认的拷贝构造函数,比如上面的程序中,我们加上 Human hu("zhongguo",1000000);此时Human hu2 = hu,这句就是调用了拷贝构造函数,Human hu3(hu)这句也是。
例如下面的代码:

#include <iostream>
#include <Windows.h>
#include <string>

using namespace std;

// 定义一个“人类”
class Human {
public:
	Human(int age, int salary);//自定义的有参构造函数
	Human(const Human&);//自定义的拷贝构造函数

	string getName();
	int getAge();
	int getSalary();

private:
	string name = "zhonguo";
	int age = 28;
	int salary;
};

//自定义有参构造函数,并且参数不全
Human::Human(int age, int salary) {
	cout << "调用自定义的有参构造函数" << endl;
	this->age = age;      //this是一个特殊的指针,指向这个对象本身
	this->salary = salary;
	name = "无名";
}

//拷贝构造函数都是引用
//执行Human  h2 = h1; 这句时会被调用,man.name 相当于h1.name只是赋值给了h2.name
Human::Human(const Human& man) {
	cout << "调用自定义的拷贝构造函数" << endl;
	name = man.name;
	age = man.age;
	salary = man.salary;
}


string Human::getName() {
	return name;
}

int Human::getAge() {
	return age;
}

int Human::getSalary() {
	return salary;
}


int main(void) {
	Human  h1(25, 35000);  // 使用自定义的默认构造函数
	Human  h2 = h1;  // 使用自定义的拷贝构造函数
	//下面h2的三个成员变量的值得打印与h1完全相同
	cout << "==============================" << endl;
	cout << "姓名:" << h2.getName() << endl;
	cout << "年龄: " << h2.getAge() << endl;
	cout << "薪资:" << h2.getSalary() << endl;
	system("pause");
	return 0;
}

上面代码中自定义了一个拷贝构造函数,但其实在这种情况使用默认的拷贝构造函数也是一样的,但有些情况下使用默认的拷贝构造函数是有危险的。

因为自动生成的构造函数(也叫合成的拷贝构造函数)它属于“浅拷贝”,或者叫位拷贝

浅拷贝的情况下,如果一个类的成员是指针的话就会出错

见下面示例:

#include <iostream>
#include <Windows.h>
#include <string>
#include <string.h>

using namespace std;

// 定义一个“人类”
class Human {
public:
	Human(int age, int salary);
	//Human(const Human&);  //不定义拷贝构造函数,编译器会生成“合成的拷贝构造函数”

	string getName();
	int getAge();
	int getSalary();
	void setAddr(const char* newAddr);
	const char* getAddr();

private:
	string name = "Unknown";
	int age = 28;
	int salary;
	//成员变量中直接定义一个指针
	char* addr;
};


Human::Human(int age, int salary) {
	cout << "调用自定义的有参构造函数" << endl;
	this->age = age;      //this是一个特殊的指针,指向这个对象本身
	this->salary = salary;
	name = "无名";//将name从unknow换成无名

	addr = new char[64];
	//strcpy_s函数是C++中的复制字符串的函数,头文件<string.h>
	strcpy_s(addr, 64, "China");
}

string Human::getName() {
	return name;
}

int Human::getAge() {
	return age;
}

int Human::getSalary() {
	return salary;
}

void Human::setAddr(const char* newAddr) {
	if (!newAddr) {
		return;
	}

	strcpy_s(addr, 64, newAddr);
}

const char* Human::getAddr() {
	return addr;
}
int main(void) {
	Human  h1(25, 35000);  // 使用自定义的默认构造函数
	Human  h2 = h1;  // 使用自定义的拷贝构造函数

	cout << "h1 addr:" << h1.getAddr() << endl;
	cout << "h2 addr:" << h2.getAddr() << endl;

	h1.setAddr("长沙");

	cout << "h1 addr:" << h1.getAddr() << endl;
	cout << "h2 addr:" << h2.getAddr() << endl;

	system("pause");
	return 0;
}

结果为:

在这里插入图片描述
发现一个什么问题,如果使用自动生成的拷贝构造函数,Human h2 = h1;

如果类中存在指针成员的话,那么就相当于固定在一起了,h1改变会引起h2的改变

C++ strcpy_s和strncpy_s使用方法

解决方案:在自定义的拷贝构造函数中,使用”深拷贝

#include <iostream>
#include <Windows.h>
#include <string>
#include <string.h>

using namespace std;

// 定义一个“人类”
class Human {
public:
	Human(int age, int salary);
	Human(const Human &man);  //如果不定义拷贝构造函数,编译器会生成“合成的拷贝构造函数”

	string getName();
	int getAge();
	int getSalary();
	void setAddr(const char* newAddr);
	const char* getAddr();

private:
	string name = "Unknown";
	int age = 28;
	int salary;
	//成员变量中直接定义一个指针
	char* addr;
};
Human::Human(const Human & man) {
	cout << "调用了自定义的拷贝构造函数" << endl;
	age = man.age;
	name = man.name;
	salary = man.salary;
	//使用自定义的拷贝构造函数进行深拷贝
	addr = new char[64];
	strcpy_s(addr,64,man.addr);
}


Human::Human(int age, int salary) {
	cout << "调用自定义的有参构造函数" << endl;
	this->age = age;      //this是一个特殊的指针,指向这个对象本身
	this->salary = salary;
	name = "无名";//将name从unknow换成无名

	addr = new char[64];
	//strcpy_s函数是C++中的复制字符串的函数,头文件<string.h>
	strcpy_s(addr, 64, "China");
}

string Human::getName() {
	return name;
}

int Human::getAge() {
	return age;
}

int Human::getSalary() {
	return salary;
}

const char* Human::getAddr() {
	return addr;
}
void Human::setAddr(const char* newAddr) {
	if (!newAddr) {
		return;
	}
	strcpy_s(addr, 64, newAddr);
}

int main(void) {
	Human  h1(25, 35000);  // 使用自定义的默认构造函数
	Human  h2 = h1;  // 使用自定义的拷贝构造函数

	cout << "h1 addr:" << h1.getAddr() << endl;
	cout << "h2 addr:" << h2.getAddr() << endl;

	h1.setAddr("长沙");

	cout << "h1 addr:" << h1.getAddr() << endl;
	cout << "h2 addr:" << h2.getAddr() << endl;

	system("pause");
	return 0;
}

在这里插入图片描述
由上面结果能看出,h2的地址并没有发生改变

2.1 浅拷贝和深拷贝(原理及区别)

关于深拷贝和浅拷贝的区别可以根据拷贝构造函数来分析

一般默认的拷贝构造函数使用的就是浅拷贝,它是将原来对象的引用直接复制给新对象了,因为标准的拷贝构造是引用类型嘛,这个时候其实新旧对象因为引用相同所以指向的值是相同的,改变原对象的值新对象的值也会随之改变

而深拷贝是创建了一个新的对象,然后将原对象中各种属性的值拷贝过去了,这个时候如果原对象被重新赋值的话,新对象的值还是保持原来的值不变。

根据我们上面的例子,如果成员对象中存在指针的话,必须要使用深拷贝

浅拷贝
在这里插入图片描述
深拷贝

在这里插入图片描述

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

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

相关文章

第47节:cesium 热力图效果(含源码+视频)

结果示例: 完整源码: index.html中增加<script src="./static/lib/heatmap.js"></script> heatmap.js /** heatmap.js v2.0.5 | JavaScript Heatmap Library** Copyright 2008-2016 Patrick Wied <heatmapjs@patrick-wied.at> - All rights re…

了解 3DS MAX 3D摄像机跟踪设置:第 7 部分

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 在SynthEyes中跟踪素材 步骤 1 打开SynthEyes软件。 打开合成之眼 步骤 2 在跟踪素材之前&#xff0c;您需要设置首选项。因为&#xff0c;你 稍后将在 3ds Max 中工作&#xff0c;必须根据 3ds Max…

双电源并用问题与解决方案

双电源并用问题 曾经有客户在电源模块应用过程中出现过这样的应用场景&#xff0c;如下图1所示。客户使用两路电源给后端电路进行供电&#xff0c;要求在不断电的情况下切换输入电源&#xff0c;此过程中发现后端电路会出现损坏。对各个节点波形进行分析后发现&#xff0c;在给…

在VMware上安装Linux虚拟机

一&#xff0c;下载CentOS操作系统 下载地址&#xff1a;https://vault.centos.org/7.6.1810/isos/x86_64/ 打开下载网址&#xff0c;找到centos-7-x86_64-DVD-1810.iso&#xff0c;下载即可。 下载的漫长等待。。。。。 二、创建虚拟机 打开下载好的VMware。 点击创建新的虚拟…

SPI 的初识

SPI 介绍 Q: SPI 是什么&#xff1f; A: SPI是串行外设接口&#xff08;Serial Peripheral Interface&#xff09;的缩写&#xff0c;是一种高速的&#xff08;比IIC快&#xff09;&#xff0c;全双工&#xff08;IIC是半双工&#xff09;&#xff0c;同步的通信总线&#xf…

【AUTOSAR】:车载以太网

车载以太网 References参考文献车载以太网的物理连接MACPHYPHY的主从关系100BASE-T1回音消除车载以太网的应用层协议References参考文献 汽车软件通信中间件SOME/IP简述100BASE-T1以太网:汽车网络的发展车载以太网的物理连接 MAC MAC(Media Access Control介质访问)一般集成…

数字贸易时代如何高效管理水果出口外贸业务

目前中国水果出口的国家有很多&#xff0c;其中包括但不限于以下几个国家&#xff1a;美国、日本、韩国、俄罗斯、德国、法国、英国、荷兰、澳大利亚、新加坡、马来西亚、泰国、印度尼西亚、巴西、阿联酋等。2023年前5个月&#xff0c;出口目的国家/地区排名前十的分别为&#…

【网络技术】计算机网络介绍

写在前面 计算机网络是指将多台计算机连接起来&#xff0c;使它们能够相互通信和共享资源的系统。 它是现代计算机科学中的重要分支之一&#xff0c;为全球范围内的信息交流和数据传输提供了基础。 本文将介绍计算机网络的基础概念、体系结构、协议、常见问题等的知识。 一、基…

uni-app:scroll-view滚动盒子,实现横(纵)向滚动条

参照&#xff1a;scroll-view | uni-app官网 (dcloud.net.cn) 样式&#xff1a; 代码&#xff1a; <template><view class"box"><scroll-view scroll-x"true" class"scroll"><view class"box1"> <view c…

ElasticSearch搜索相关性及打分的相关原理

文章目录 一、相关性和打分简介二、TF-IDF得分计算公式三、BM25&#xff08;Best Matching 25&#xff09;四、使用explain查看TF-IDF五、通过Boosting控制相关度 一、相关性和打分简介 举个例子来说明&#xff1a; 假设有一个电商网站&#xff0c;用户在搜索框中输入了关键词&…

前端 mock 数据的几种方式

目录 接口demo Better-mock just mock koa webpack Charles 总结 具体需求开发前&#xff0c;后端往往只提供接口文档&#xff0c;对于前端&#xff0c;最简单的方式就是把想要的数据写死在代码里进行开发&#xff0c;但这样的坏处就是和后端联调前还需要再把写死的数据…

计算机视觉和滤帧技术

01 什么是计算机视觉 进入主题之前&#xff0c;先聊聊计算机视觉。 计算机视觉是指利用摄像头和电脑识别、跟踪和测量目标&#xff0c;并进行图像处理&#xff0c;使其适合人眼观察或仪器检测。作为一个科学学科&#xff0c;计算机视觉模拟生物视觉&#xff0c;旨在实现图像和视…

DAY48:动态规划(十一)爬楼梯(进阶版)+零钱兑换(理解DP数组“装满“含义)

文章目录 70.爬楼梯&#xff08;改版题目&#xff09;思路遍历顺序 完整版总结面试情况 322.零钱兑换&#xff08;DP数组含义的进一步理解&#xff09;思路DP数组含义递推公式遍历顺序初始化最开始的写法debug逻辑错误&#xff1a;背包不一定装满 修改完整版递归逻辑分析背包是…

极值理论 EVT、POT超阈值、GARCH 模型分析股票指数VaR、条件CVaR:多元化投资组合预测风险测度分析...

全文链接&#xff1a;http://tecdat.cn/?p24182 本文用 R 编程语言极值理论 (EVT) 以确定 10 只股票指数的风险价值&#xff08;和条件 VaR&#xff09;&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 使用 Anderson-Darling 检验对 10 只股票的组合数据进行…

EFCore—context在其他程序集时如何进行数据迁移

场景 解决方案&#xff1a; 代码示例&#xff1a; 场景 一般来说&#xff0c;如果efcore进行数据迁移的步骤如下 安装nuget包创建实体类创建config创建dbcontext 然后执行如下命令就可以成功迁移了 Add-Migration Init Update-Database 一执行&#xff0c;报错 Unable t…

(四)「消息队列」之 RabbitMQ 路由(使用 .NET 客户端)

0、引言 先决条件 本教程假设 RabbitMQ 已安装并且正在 本地主机 的标准端口&#xff08;5672&#xff09;上运行。如果您使用了不同的主机、端口或凭证&#xff0c;则要求调整连接设置。 获取帮助 如果您在阅读本教程时遇到问题&#xff0c;可以通过邮件列表或者 RabbitMQ 社区…

k8s之Pod容器的探针

目录 一、Pod 容器的探针二、探针的探测方式2.1 存活探针的使用2.1.1 exec方式2.1.2 httpGet方式2.1.3 tcpSocket方式 2.2 就绪探针的使用2.3 启动探针的使用2.4 7.Pod 容器的启动和退出动作 三、总结 一、Pod 容器的探针 探针是由 kubelet 对容器执行的定期诊断&#xff08;p…

M1安装ParallelsDesktop-18 重启镜像后无网络

M1安装ParallelsDesktop-18 重启镜像后无网络 重新关联镜像 sudo -b /Applications/Parallels\ Desktop.app/Contents/MacOS/prl_client_app

手机定屏死机问题操作指南

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 一、定屏死机问题抓取 Log 要求二、 复现定屏死机问题后做什么三、检查adb是否可连的方法四、连接adb 抓取以下Log五、如果adb不可连&#xff0c;执行下…

一、数制及其转换

目录 常用进制介绍 十进制 二进制 八进制 十六进制 进制转换 二进制转十进制 十进制转二进制 二进制转十六进制 十六进制转二进制 二进制转八进制 八进制转二进制 十六进制转八进制 八进制转十六进制 常用进制介绍 十进制 介绍&#xff1a;十进制是日常生活中最…