深拷贝vs浅拷贝,引用传递vs值传递vs指针传递

一、值传递

函数调用时传递对象,会调用拷贝构造函数(可能是浅拷贝或深拷贝,取决于类如何定义)

二、引用传递

函数调用时传递对象的引用,不调用拷贝构造函数,直接对原值进行操作

三、指针传递

将原指针复制一份传递给函数,本质也属于值传递,在函数内部可以修改副本指针指向的地址(不影响原指针的指向),或者可以用*解引用来直接修改原地址的值。

四、三种传递方法的示例和总结

#include <stdio.h>

//此函数需要在C++中运行,C语言中没有引用传递,在C语言中此函数会报错,在C语言可以用指针传递代替引用传递
int setValue(int& a,int b){  // 变量a是引用传递,直接对原始变量进行操作,变量b是值传递,会拷贝原变量b生成副本
    a=3;  // 修改的是原变量
    b=6;  // 修改的是局部副本
    return a+b;  // 返回 3+6=9
}


int setValue2(int* a,int b){  // 变量a是指针传递,会拷贝原变量的地址,变量b是值传递,会拷贝原变量f生成副本
    printf("局部变量a的值:%p\n",a);//可以发现a的值就是原变量的地址
    *a=3;  // 通过解引用修改地址中的值,等同于修改的是原变量
    b=6;  // 修改的是局部副本
    return *a+b;  // 返回 3+6=9
}


int main(){
    //对比引用传递和值传递
    int a=5;
    int b=5;
    int c = setValue(a,b);
    printf("a:%d, b:%d, c:%d\n",a,b,c);//可以发现a被修改了,但是b未被修改

    //对比指针传递和值传递
    int e=5;
    int f=5;
    int g = setValue2(&e,f);
    printf("原变量e的地址:%p\n",&e);
    printf("e:%d, f:%d, g:%d\n",e,f,g);//可以发现e被修改了,但是f未被修改
} 

特性值传递指针传递引用传递
语言支持C/C++C/C++C++特有
传递内容变量的副本变量的地址变量的别名
修改原值不可能可以(需解引用)可以直接修改
内存开销复制整个数据固定大小(指针大小)通常无额外开销
空值安全总是安全需要检查空指针必须绑定有效对象

五、浅拷贝

默认的拷贝构造函数(只复制指针,不复制指针指向的数据)

六、深拷贝

自定义拷贝构造函数(复制指针指向的数据)

七、两种拷贝方式示例

#include <iostream>
#include <cstring>
using namespace std;
class Person {
private:
    
    int age;
    
public:
    char* name;  // 动态分配的姓名
    // 构造函数
    Person(const char* name, int age) : age(age) {
        this->name = new char[strlen(name) + 1];
        strcpy(this->name, name);
        std::cout << "构造函数: " << name << std::endl;
    }
    
    // 浅拷贝构造函数(危险!)
    Person(const Person& other) : name(other.name), age(other.age) {
        std::cout << "浅拷贝构造函数: " << name << std::endl;
    }
    
    // 析构函数
    ~Person() {
        std::cout << "析构函数: " << name << std::endl;
        delete[] name;
    }
    
    void print() const {
        std::cout << "姓名: " << name << ", 年龄: " << age << std::endl;
    }
    
    void setName(const char* newName) {
        strcpy(name, newName);
    }
};

class Person2 {
private:
    
    int age;
    
public:
    char* name;  // 动态分配的姓名
    // 构造函数
    Person2(const char* name, int age) : age(age) {
        this->name = new char[strlen(name) + 1];
        strcpy(this->name, name);
        std::cout << "构造函数: " << name << std::endl;
    }
    
    //深拷贝构造函数(安全)
    Person2(const Person2& other) : age(other.age) {
        name = new char[strlen(other.name) + 1];
        strcpy(name, other.name);
        std::cout << "深拷贝构造函数: " << name << std::endl;
    }
    
    // 析构函数
    ~Person2() {
        std::cout << "析构函数: " << name << std::endl;
        delete[] name;
    }
    
    void print() const {
        std::cout << "姓名: " << name << ", 年龄: " << age << std::endl;
    }
    
    void setName(const char* newName) {
        strcpy(name, newName);
    }
};
 
// 值传递函数
void passByValue1(Person p) {
    std::cout << "值传递内部: ";
    p.print();
    p.setName("内部修改");
}

// 值传递函数
void passByValue2(Person2 p) {
    std::cout << "值传递内部: ";
    p.print();
    p.setName("内部修改");
}
 
// 引用传递函数  
void passByReference1(Person& p) {
    std::cout << "引用传递内部: ";
    p.print();
    p.setName("内部修改");
}

// 引用传递函数  
void passByReference2(Person2& p) {
    std::cout << "引用传递内部: ";
    p.print();
    p.setName("内部修改");
}
 
int main() {
    std::cout << "\n=== 引用传递 (高效安全) ===" << std::endl;
    {
        Person charlie("Charlie", 35);
        passByReference1(charlie);  // 引用传递,不拷贝
        std::cout << "引用传递后原始对象: ";
        charlie.print();  // 原始对象被修改
    }  // 正常析构

    std::cout << "\n=== 深拷贝 + 值传递 (安全示例) ===" << std::endl;
    {
        // 使用深拷贝构造函数的情况
        Person2 bob("Bob", 30);
        passByValue2(bob);  // 值传递触发深拷贝
        std::cout << "bob.name指针值: " << static_cast<void*>(bob.name) << std::endl;
        std::cout << "值传递后原始对象: ";
        bob.print();  // 原始对象不受影响
    }  // 正常析构,没有内存问题

    std::cout << "=== 浅拷贝 + 值传递 (危险示例) ===" << std::endl;
    {
        Person alice("Alice", 25);
        passByValue1(alice);  // 值传递触发浅拷贝
        std::cout << "引用传递后原始对象: ";
        alice.print();  // 原始对象被修改
        //这里会导致双重释放错误!因为在passByValue中采用浅拷贝构造了对象p,p的name指针和alice的name指针指向同一块内存,而在passByValue函数运行结束p对象被释放,此刻alice的name指针就变成了悬空指针
        //而在出代码块alice会调用析构导致alice.name双重释放,此时可能出现乱码,原因:析构函数尝试读取已释放内存的内容,读到的是一些随机数据(内存垃圾)
    }
    
    return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10710

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值