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️⃣ 用对象初始化对象数组

不会调用

  • 引用参数
  • 返回引用
相关推荐
努力中的编程者2 小时前
栈和队列(C语言底层实现栈)
c语言·开发语言·数据结构·c++
SunnyDays10112 小时前
使用 Python 轻松操控 Excel 网格线:隐藏、显示与自定义颜色
开发语言·python·excel
落叶@Henry2 小时前
C# async 和await 的面试题
开发语言·c#
大鹏说大话2 小时前
打破边界:前后端分离架构下的跨域难题与破局之道
开发语言
前端不太难2 小时前
OpenClaw 如何运行 Claw 资源文件
c++·开源·游戏引擎
草莓熊Lotso2 小时前
MySQL 数据类型核心指南:选型、实战与避坑
linux·运维·服务器·数据库·c++·人工智能·mysql
亚马逊云开发者2 小时前
MCP 协议实战:用 Amazon Bedrock 让 AI Agent 安全调用云服务的完整方案
开发语言·qt·安全
co_wait2 小时前
【C++ STL】排序算法
开发语言·c++·排序算法
黑眼圈子2 小时前
Java正则表达式基础知识
java·开发语言·正则表达式