C++:拷贝构造函数

cpp 复制代码
Demo
#include <iostream>
#include <Windows.h>
#include <string> 
#include <string.h>
 
using namespace std;
 
// 定义一个"人类"
class Human { 
public:
    Human();
    Human(int age, int salary);
    Human(const Human&); //不定义拷贝构造函数,编译器会生成"合成的拷贝构造函数"
 
    void eat(); 
    void sleep(); 
    void play(); 
    void work();
 
    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() {
    name = "无名氏"; 
    age = 18; 
    salary = 30000;
}
 
Human::Human(int age, int salary) {
    cout << "调用自定义的构造函数" << endl; 
    this->age = age;	//this是一个特殊的指针,指向这个对象本身 
    this->salary = salary; 
    name = "无名";
    addr = new char[64]; 
    strcpy_s(addr, 64, "China");
}
 
Human::Human(const Human &man) {
    cout << "调用自定义的拷贝构造函数" << "参数:" << &man << " 本对象:" << this << endl;
 
    age = man.age;	//this是一个特殊的指针,指向这个对象本身 
    salary = man.salary; 
    name = man.name;
 
    // 深度拷贝
    addr = new char[64]; 
    strcpy_s(addr, 64, man.addr);
}
 
void Human::eat() {
    cout << "吃炸鸡,喝啤酒!" << endl;
}
 
void Human::sleep() {
    cout << "我正在睡觉!" << endl;
}
 
void Human::play() {
    cout << "我在唱歌! " << endl;
}
 
void Human::work() { 
    cout << "我在工作..." << endl;
}
 
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;
}
 
void test(Human man) { 
    cout << man.getSalary() << endl;
}
 
void test2(Human &man) { //不会调用拷贝构造函数,此时没有没有构造新的对象
    cout << man.getSalary() << endl;
}
 
Human test3(Human &man) {
    return man;
}
 
Human& test4(Human &man) {
    return man;
}
 
int main(void) {
    Human h1(25, 35000); // 调用默认构造函数
    Human h2(h1);	// 调用拷贝构造函数 
    Human h3 = h1;	// 调用拷贝构造函数
 
    test(h1); // 调用拷贝构造函数 
    test2(h1); // 不会调用拷贝构造函数
    test3(h1);	// 创建一个临时对象,接收test3函数的返回值,调用1次拷贝构造函数 
 
    Human h4 = test3(h1); // 仅调用1次拷贝构造函数,返回的值直接作为h4的拷贝构造函数的参数                                             
    test4(h1);	// 因为返回的是引用类型,所以不会创建临时对象,不会调用拷贝构造函数
    Human men[] = { h1, h2, h3 }; //调用3次拷贝构造函数
    system("pause"); return 0;
}

一、什么是拷贝构造函数

拷贝构造函数的标准形式:

cpp 复制代码
Human(const Human& other);

作用:

用一个已有对象,创建一个新的对象

例如:

cpp 复制代码
Human h1(25,35000);
Human h2(h1);   // 用 h1 创建 h2

这里会调用:

复制代码
Human(const Human& man)

二、什么时候会调用拷贝构造函数

你的笔记总结是正确的,主要有 4种情况。


1 对象初始化另一个对象

cpp 复制代码
Human h1(25,35000);
Human h2(h1);     // 调用
Human h3 = h1;    // 也调用

注意:

cpp 复制代码
Human h3 = h1;

不是赋值

而是

用 h1 初始化 h3

所以调用 拷贝构造函数


2 函数参数是对象(值传递)

函数:

cpp 复制代码
void test(Human man)

调用:

cpp 复制代码
test(h1);

执行过程:

复制代码
h1  ----> 复制 ----> man

所以:

调用一次拷贝构造函数


如果是引用:

cpp 复制代码
void test2(Human &man)

调用:

cpp 复制代码
test2(h1);

执行:

复制代码
h1  ------> 直接引用

没有创建新对象。

所以:

不会调用拷贝构造函数


3 函数返回对象

函数:

cpp 复制代码
Human test3(Human &man) {
    return man;
}

返回类型:

复制代码
Human

调用:

cpp 复制代码
test3(h1);

执行过程:

复制代码
man  ----> 复制 ----> 临时对象

所以:

调用拷贝构造函数


如果接收:

cpp 复制代码
Human h4 = test3(h1);

现代编译器会优化(RVO):

复制代码
man ----> h4

所以:

只调用1次拷贝构造


4 用对象初始化对象数组

代码:

cpp 复制代码
Human men[] = { h1, h2, h3 };

等价于:

复制代码
men[0] = h1
men[1] = h2
men[2] = h3

每个元素都要:

复制代码
拷贝一次

所以:

复制代码
调用3次拷贝构造函数

三、什么时候不会调用

1 引用参数

cpp 复制代码
void test2(Human &man)

因为:

复制代码
没有创建新对象

2 返回引用

cpp 复制代码
Human& test4(Human &man)

返回:

复制代码
man 的引用

所以:

复制代码
不会产生临时对象

四、你的程序完整调用流程

假设运行 main:

cpp 复制代码
Human h1(25, 35000);

输出

复制代码
调用自定义的构造函数

cpp 复制代码
Human h2(h1);

输出

复制代码
调用自定义的拷贝构造函数

cpp 复制代码
Human h3 = h1;

输出

复制代码
调用自定义的拷贝构造函数

cpp 复制代码
test(h1);

输出

复制代码
调用自定义的拷贝构造函数

cpp 复制代码
test2(h1);

不调用


cpp 复制代码
test3(h1);

调用一次


cpp 复制代码
Human h4 = test3(h1);

调用一次(编译器优化)


cpp 复制代码
test4(h1);

不调用


cpp 复制代码
Human men[] = { h1, h2, h3 };

调用3次


五、为什么要写拷贝构造函数(最重要)

因为你有:

cpp 复制代码
char *addr;

如果不用 深拷贝

编译器默认拷贝:

复制代码
addr = man.addr

结果:

复制代码
两个对象指向同一块内存

后果:

复制代码
释放内存时 double free

所以你写了:

cpp 复制代码
addr = new char[64];
strcpy_s(addr,64,man.addr);

这叫:

深拷贝


六、一句话总结(考试必背)

拷贝构造函数调用的四种情况

1️⃣ 用对象初始化对象

2️⃣ 函数参数是对象(值传递)

3️⃣ 函数返回对象(非引用)

4️⃣ 用对象初始化对象数组

不会调用

  • 引用参数
  • 返回引用
相关推荐
Demon--hx13 分钟前
jsoncpp 核心概念梳理:命名空间、Value 容器与格式化输出
c++
不是万能的豆学长3 小时前
filetidy 文件整理助手:一键归类 + 规范重命名(Python 零依赖单文件工具)
大数据·开发语言·python·技术美术
懂点Tech7 小时前
ctfshow MISC入门图片篇(信息附加):misc41解题思路
开发语言·python
catchadmin8 小时前
PHP 8.7 协程新提案 让跨协程代码共享请求身份
开发语言·php
念何架构之路9 小时前
go-grpc设计思想
开发语言·后端·golang
酷炫码神10 小时前
Day 10|Pinia 状态管理:把散落在各组件的数据,收进一个全局仓库
android·开发语言·javascript
Elastic 中国社区官方博客10 小时前
我们如何通过 70 多个拉取请求,将 OpenTelemetry Java agent 迁移到 invokedynamic
java·大数据·开发语言·elasticsearch·搜索引擎·全文检索·es
tyn18811 小时前
记录本地安装nacos服务的方法及异常
java·开发语言
步行cgn11 小时前
Spring MVC 视图机制的核心接口与类
java·开发语言·后端·spring·mvc
传奇开心果编程12 小时前
【Rust入门练中学】第5课:引用与借用
开发语言·rust·引用·编程语言理论·通用编程语言·借用