C++ class传值和传引用的详细介绍

 更新时间:2026年04月02日 09:58:39   作者:平凡而伟大.  
在 C++ 中,class(类对象)的传值和传引用是两种截然不同的参数传递方式,它们在性能、内存管理和行为上有着本质的区别,本文详细对比了C++中对象传值和传引用两种参数传递方式的差异,感兴趣的朋友跟随小编一起看看吧

在 C++ 中,class(类对象)的传值传引用是两种截然不同的参数传递方式,它们在性能、内存管理和行为上有着本质的区别。

简单来说:传值是“复制一份”,传引用是“取个别名”。

以下是详细的对比分析:

1. 核心区别概览

表格

特性传值传引用 (&)
机制创建对象的副本 (拷贝)对象的别名 (直接操作原对象)
修改影响函数内修改不影响外部对象函数内修改直接影响外部对象
性能开销 (需调用拷贝构造函数) (无拷贝,仅传递地址)
内存占用占用双倍内存 (原对象+副本)几乎不占额外内存
安全性高 (外部数据受保护)较低 (外部数据可能被意外修改)
多态性会导致对象切片 (丢失派生类信息)支持多态 (保留完整信息)

2. 深入解析

📦 传值

当你按值传递一个类对象时,C++ 会调用该类的拷贝构造函数来创建一个完全独立的副本。

  • 优点
    • 数据安全:函数内部无论怎么折腾,都不会影响原始数据。
    • 语义清晰:适合传递小型对象或不需要修改的场景。
  • 缺点
    • 性能差:如果类很大(包含大量成员变量或动态内存),拷贝操作非常耗时。
    • 对象切片:如果传递的是派生类对象给基类形参,派生类特有的部分会被“切掉”,导致多态失效。

🔗 传引用

传递引用实际上是传递对象的内存地址,但在语法上看起来像直接操作变量。

  • 优点
    • 高性能:没有拷贝开销,无论对象多大,传递效率都一样高。
    • 支持多态:可以传递派生类对象给基类引用,正确调用虚函数。
    • 修改原值:允许函数直接修改调用者的数据(如果需要的话)。
  • 缺点
    • 副作用风险:如果不小心,可能会意外修改外部数据。

3. 代码示例

#include <iostream>
#include <string>
#include <vector>
class Student {
public:
    std::string name;
    int age;
    Student(std::string n, int a) : name(n), age(a) {}
    // 拷贝构造函数(用于演示传值时的拷贝行为)
    Student(const Student& other) : name(other.name), age(other.age) {
        std::cout << "📋 [拷贝构造函数被调用]" << std::endl;
    }
};
// --- 1. 传值 ---
// 这里的 s 是 originalStudent 的副本
void modifyByValue(Student s) {
    s.name = "Modified_Value";
    s.age = 99;
    std::cout << "函数内(值): " << s.name << ", " << s.age << std::endl;
}
// --- 2. 传引用 (非常用) ---
// 这里的 s 是 originalStudent 的别名
void modifyByReference(Student& s) {
    s.name = "Modified_Reference";
    s.age = 88;
    std::cout << "函数内(引用): " << s.name << ", " << s.age << std::endl;
}
// --- 3. 常量引用 (最佳实践) ---
// 既避免了拷贝,又防止了修改
void printStudent(const Student& s) {
    // s.name = "Error"; // 编译错误:不能修改 const 对象
    std::cout << "函数内(只读): " << s.name << ", " << s.age << std::endl;
}
int main() {
    Student originalStudent("Alice", 20);
    std::cout << "原始数据: " << originalStudent.name << ", " << originalStudent.age << std::endl;
    std::cout << "--- 调用传值函数 ---" << std::endl;
    modifyByValue(originalStudent); 
    // originalStudent 未被修改,但触发了拷贝构造
    std::cout << "调用后: " << originalStudent.name << ", " << originalStudent.age << std::endl << std::endl;
    std::cout << "--- 调用传引用函数 ---" << std::endl;
    modifyByReference(originalStudent);
    // originalStudent 被修改了
    std::cout << "调用后: " << originalStudent.name << ", " << originalStudent.age << std::endl;
    return 0;
}

4. 什么时候用哪个?(最佳实践)

在实际开发中,请遵循以下规则:

  • 首选:const 引用 (const ClassName& obj)
    • 场景:绝大多数情况。当你只需要读取对象数据,不需要修改它时。
    • 理由:避免了昂贵的拷贝开销,同时保证了数据安全(不会被函数修改)。
    • 例子: void printData(const std::vector<int>& data);
  • 次选:非 const 引用 (ClassName& obj)
    • 场景:当你需要在函数内部修改对象状态时。
    • 理由:高效且意图明确(调用者知道数据会被改)。
    • 例子: void updateConfig(Config& cfg);
  • 最后:传值 (ClassName obj)
    • 场景:
    • 对象非常小(如只包含 1-2 个 int 的类)。
    • 你确实需要一个副本,并且要在函数内随意修改而不影响原对象。
    • 注意:对于像 std::vectorstd::string 或大型自定义类,永远不要按值传递。

⚠️ 特别注意:对象切片

如果你使用继承和多态,千万不要传值

class Animal { public: virtual void speak() { cout << "..." << endl; } };
class Dog : public Animal { public: void speak() { cout << "Woof!" << endl; } };
void makeSound(Animal a) { a.speak(); } // 错误:传值会导致切片,Dog 变成 Animal
void makeSound(Animal& a) { a.speak(); } // 正确:传引用保留多态性

到此这篇关于C++ class传值和传引用的详细介绍的文章就介绍到这了,更多相关C++ class传值和传引用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言中的冒泡排序问题

    C语言中的冒泡排序问题

    这篇文章主要介绍了C语言中的冒泡排序问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • C语言详解分析进程控制中进程终止的实现

    C语言详解分析进程控制中进程终止的实现

    当进程完成执行最后语句并且通过系统调用 exit() 请求操作系统删除自身时,进程终止。这时,进程可以返回状态值(通常为整数)到父进程(通过系统调用 wait())。所有进程资源,如物理和虚拟内存、打开文件和 I/O 缓冲区等,会由操作系统释放
    2022-08-08
  • 基于VC 6.0使用C语言实现俄罗斯方块

    基于VC 6.0使用C语言实现俄罗斯方块

    这篇文章主要为大家详细介绍了基于VC 6.0使用C语言实现俄罗斯方块,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-06-06
  • Pipes实现LeetCode(192.单词频率)

    Pipes实现LeetCode(192.单词频率)

    这篇文章主要介绍了Pipes实现LeetCode(192.单词频率),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • 用C++编写扩展node.js(node-ffi版)

    用C++编写扩展node.js(node-ffi版)

    今天小编就为大家分享一篇关于用C++编写扩展node.js(node-ffi版),小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • C++实现LeetCode(190.颠倒二进制位)

    C++实现LeetCode(190.颠倒二进制位)

    这篇文章主要介绍了C++实现LeetCode(190.颠倒二进制位),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • C++实现leetcode(3.最长无重复字符的子串)

    C++实现leetcode(3.最长无重复字符的子串)

    这篇文章主要介绍了C++实现leetcode(3.最长无重复字符的子串),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • 关于背包问题的一些理解和应用

    关于背包问题的一些理解和应用

    这篇文章主要介绍了关于背包问题的一些理解和应用,本文可以说是背包问题九讲的补充、读后感,需要的朋友可以参考下
    2014-08-08
  • 如何用C写一个web服务器之GCC项目编译

    如何用C写一个web服务器之GCC项目编译

    本文主要介绍了,如何用C写一个web服务器,Linux下用GCC进行项目编译,对此感兴趣的同学,可以参考下。
    2021-05-05
  • C++实现数据文件存储与加载

    C++实现数据文件存储与加载

    这篇文章主要为大家详细介绍了C++实现数据文件存储与加载,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-06-06

最新评论