c++中值传递时是如何触发拷贝构造函数的

当函数的参数是类的对象(而非指针/引用)时,调用函数传递参数的过程就是值传递,此时编译器会自动调用拷贝构造函数,创建一个实参的"副本"作为函数的形参。

一、值传递触发拷贝构造函数的完整过程

可以把这个过程想象成"复制粘贴"

1、你准备把一个已存在的对象(实参)传给函数

2、因为是值传递,函数不会直接使用原对象,而是需要一个"独立的副本"

3、编译器会调用该类的拷贝构造函数,用原对象(实参)为模板,创建一个新的对象(形参);

4、函数内部操作的是这个"副本",函数执行完毕后,副本会被销毁(调用析构函数)

5、原对象不受函数内操作的影响

二、代码示例

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

class Person {
public:
    string name;
    
    // 带参构造函数
    Person(string n) : name(n) {
        cout << "[构造] 创建对象:" << name << endl;
    }

    // 拷贝构造函数(核心:值传递时会调用)
    Person(const Person& other) {
        this->name = other.name + "(副本)"; // 给副本加标记,方便识别
        cout << "[拷贝构造] 为值传递创建副本:" << this->name << endl;
    }

    // 析构函数
    ~Person() {
        cout << "[析构] 销毁对象:" << name << endl;
    }

    // 成员函数:修改名字(仅影响副本)
    void changeName(string newName) {
        this->name = newName;
        cout << "[函数内] 修改副本名字为:" << this->name << endl;
    }
};

// 函数参数为值传递(Person对象,而非指针/引用)
void testValuePass(Person p) {
    p.changeName("修改后的副本");
}

int main() {
    // 1. 创建原对象
    Person p1("原对象-张三");
    cout << "------------------------" << endl;

    // 2. 调用函数,值传递参数 → 触发拷贝构造
    testValuePass(p1);
    cout << "------------------------" << endl;

    // 3. 查看原对象:未被修改
    cout << "[主函数] 原对象名字:" << p1.name << endl;

    return 0;
}
控制台
[构造] 创建对象:原对象-张三
------------------------
[拷贝构造] 为值传递创建副本:原对象-张三(副本)
[函数内] 修改副本名字为:修改后的副本
[析构] 销毁对象:修改后的副本
------------------------
[主函数] 原对象名字:原对象-张三
[析构] 销毁对象:原对象-张三

三、关键细节拆解

1、触发时机

执行testValuePass(p1)时,编译器发现参数是值传递(Person p),立即调用Person(const Person& other);

拷贝构造函数的参数other就是原对象p1,函数内创建的p是p1的副本。

2、为什么必须是拷贝构造?

如果没有显式定义拷贝构造函数,编译器会生成默认的浅拷贝构造函数,依然会触发(只是没有日志输出);

只有参数是const 类名&的构造函数,才是拷贝构造函数,这是 C++ 的语法规则。

3、对比:避免拷贝构造的方式

如果不想触发拷贝构造(提升性能,避免深拷贝开销),可以把参数改成引用传递

cpp 复制代码
// 引用传递:直接操作原对象,不触发拷贝构造
void testRefPass(Person& p) {
    p.changeName("修改原对象");
}
调用testRefPass(p1)时,不会调用拷贝构造函数,函数内修改的是原对象p1。

四、常见误区澄清

×误区:"值传递只是把对象的值复制过去,和拷贝构造无关";

√正解:C++ 中,类对象的 "值复制" 本质就是通过拷贝构造函数完成的,这是面向对象的核心规则。

五、总结

1、触发条件:函数参数为类对象(值传递)时,编译器会自动调用拷贝构造函数,创建实参的副本作为形参。

2、过程本质:值传递的 "复制" = 拷贝构造函数的调用 + 新对象(副本)的创建。

3、优化建议:如果不需要修改原对象,用const 类名&(const 引用)传递参数,既不触发拷贝构造,又能防止函数内修改原对象。

相关推荐
WL学习笔记1 小时前
C & C++ 内存管理
c++·c++内存管理
顽疲2 小时前
Java vue 养老系统源码详解:月账单 care_month_bill 与 Quartz 扣费 Job 实践
java·开发语言·vue.js·vue养老·springboot养老·java养老
Wang's Blog2 小时前
Java 项目实战: 交友项目 - 安装开发工具与环境准备
java·开发语言·交友
宛 禾4 小时前
Qt 控件入门:QLabel 从拖拽到对象树,一篇讲透
c++·qt·学习
hetao17338374 小时前
联盟场-提高组-R7 & ZYZSC-S-Round 11
c++·算法
程序喵大人4 小时前
【C++入门】编译链接模型 - 07 链接器怎样把符号对上号
开发语言·c++·编译器·编译链接·链接器
微三云 - 廖会灵 (私域系统开发)4 小时前
供应链系统实战:多渠道订单聚合与分布式库存一致性方案总结
开发语言·分布式·php
局部有解4 小时前
Hot100 - 三数之和
数据结构·c++·算法
学逆向的4 小时前
C 数据溢出与精度丢失
c语言·开发语言·网络安全
longlongzihan4 小时前
C++ 常用数据结构速查:vector、stack、queue、list、树、图、哈希表等声明与核心操作
数据结构·c++·list