c++的拷贝构造函数和赋值函数

拷贝构造函数和赋值函数

什么是拷贝构造

是一种特殊构造函数,如果没有显式的实现,编译器就会自动生成。

class 类名
{
public:
    // 拷贝构造
    类名(const 类名& that)
    {
        
    }
};
什么时候会调用拷贝构造

当使用一个类对象给另一个新的类对象初始化时,就会自动调用拷贝构造。

#include <iostream>
using namespace std;
​
class Test
{
public:
    Test(void)
    {   
        cout << "调用了普通的构造函数" << endl;
    }   
    Test(const Test& that)
    {   
        cout << "调用了拷贝构造" << endl;
    }   
};
​
void func(Test t)
{
​
}
​
int main(int argc,const char* argv[])
{
    Test t1;        // 调用的是普通构造
    Test t2 = t1;   // 调用的是拷贝构造
    func(t1);       // 调用的是拷贝构造
    
    return 0;
}
拷贝构造的任务是什么

拷贝构造参数对象的所有成员变量挨个赋值给新对象的成员变量,一般情况下编译器自动生成的拷贝构造就能完全满足我们使用需求。

什么时候需要显式实现拷贝构造

当成员变量中有指针成员且指向了堆内存,就需要显式实现拷贝构造。

编译器自动生成的拷贝构造,只会对成员变量挨个赋值,如果成员变量中有指针变量且指向堆内存,结果就两个对象的指针变量同时指向一份堆内存,当它们执行析构函数时,会把这块堆内存释放两次,产生 double free or corruption 的错误。

正确的做法应该是先给新对象的指针变量重新申请一份堆内存,然后把旧对象的指针变量所指向的内存拷贝到新对象的指针变量所指向的内存。

#include <iostream>
using namespace std;
​
class Test
{
    int* ptr;
public:
    Test(int num)
    {
        ptr = new int;
        cout << "new:" << ptr << endl;
        *ptr = num;
    }
​
    ~Test(void)
    {
        cout << "delete:" << ptr << endl;
        delete ptr;
    }
​
    /* 编译器生成的拷贝构造,会造成 double free
    Test(const Test& that)
    {
        ptr = that.ptr; 
    }
    */
    Test(const Test& that)
    {
        // 给新对象的指针变量重新申请堆内存
        ptr = new int(*that.ptr);
        // 把旧对象的指针变量所指向的内存拷贝给新对象的指针变量所指向的内存,如果不方便解引用时可以使用memcpy函数
    }
​
    void show(void)
    {
        cout << "val:" << *ptr << " addr:" << ptr << endl;
    }
};
​
int main(int argc,const char* argv[])
{
    Test t1(12345);
    Test t2 = t1;
    t1.show();
    t2.show();
​
    return 0;
}
什么是赋值函数

是一种特殊的成员函数,如果没有显式实现,编译器会自动生成。

class 类名
{
public:
    // 赋值函数
    const 类名& operator=(const 类名& that)
    {
        
    }
};
什么时候会调用赋值函数

当一个旧对象给另一个旧对象赋值时会自动调用赋值函数。

当一个旧对象给另一个新对象初始化时会自动调用拷贝构造函数。

#include <iostream>
using namespace std;
​
class Test
{
public:
    Test(const Test& that)
    {   
        cout << "调用了拷贝构造" << endl;
    }   
​
    void operator=(const Test& that)
    {   
        cout << "调用了赋值函数" << endl;
    }   
};
​
int main(int argc,const char* argv[])
{
    Test t1;        // 调用了普通的构造函数
    Test t2 = t1;   // 调用了拷贝构造
    t1 = t2;        // 调用的是赋值函数
    return 0;
}
赋值函数的任务是什么

赋值函数与拷贝构造的任务几乎相同,都是挨个给成员变量赋值,但如果需要显式实现时,它的业务逻辑不同。

什么时候需要显式实现赋值函数

当需要显式实现拷贝构造时,就需要显式实现赋值函数,它们两个面临问题是一样的。

赋值函数不应该对成员指针变量赋值,而应该对象成员指针变量所指向的内存进行拷贝。

#include <iostream>
using namespace std;
​
class Test
{
    int* ptr;
public:
    Test(int num)
    {
        ptr = new int;
        cout << "new " << ptr << endl;
        *ptr = num;
    }
    ~Test(void)
    {
        cout << "delete " << ptr << endl;
        // delete ptr;
    }
​
    Test(const Test& that)
    {
        ptr = new int;
        // 如果不方便解引用,可以调用memcpy函数进行拷贝
        *ptr = *that.ptr;
        cout << "new " << ptr << "调用了拷贝构造" << endl;
    }
​
    const Test& operator=(const Test& that)
    {
        // 当ptr和that.ptr指向的内存块大小一样,可以直接进行内存拷贝
        *ptr = *that.ptr;
        cout << "调用了赋值函数" << endl;
        return *this;
        /*
           当对象的ptr指向的内存与与that.ptr指向的内存块不一样大
           先释放旧的ptr
           再分配新的,要与that.ptr的内存块一样大
           然后再拷贝
        */
    }
};
​
int main(int argc,const char* argv[])
{
    Test t1(1234);      // 调用了普通的构造函数
    Test t2 = t1;   // 调用了拷贝构造
    t1 = t2;        // 调用的是赋值函数
    return 0;
}
浅拷贝与深拷贝

拷贝就是一个对象给另一个对象赋值,编译器自动生成的拷贝构造和赋值函数执行的业务逻辑就是浅拷贝(成员指针给成员指针赋值),深拷贝就是把成员指针所指向的内存拷贝给另一个成员指针所指向的内存。

浅拷贝就是指针给指针赋值,深拷贝就内存给内存赋值。

注意:如果成员变量中没有成员指针,则浅拷贝就可以满足需求,如果如果成员变量中有成员指针且指向堆内存,则必须手动实现深拷贝,否则就会出现 double free or corruption 的错误。

拷贝构造函数是用来创建一个新对象并将其初始化为给定对象的副本的特殊成员函数。它通常用于以下情况: - 当一个对象通过值传递给函数或以值的形式返回时 - 当一个对象用另一个对象进行初始化时 - 当一个对象作为另一个对象的成员进行初始化时 对于类`Person`的拷贝构造函数,它会接受一个`const Person&`类型的参数,并将其成员变量`name_`赋值给新创建的对象的`name_`成员变量。 赋值运算符是用于将一个对象的值分配给另一个已经存在的对象的成员函数。它通常用于以下情况: - 当一个对象被另一个对象赋值时 - 当一个对象作为另一个对象的成员进行赋值时 对于类`Person`的赋值运算符,它会接受一个`const Person&`类型的参数,并将其成员变量`name_`赋值给当前对象的`name_`成员变量。然后,它将返回一个指向左侧运算对象的引用,以支持连续赋值的操作。 如果在类定义中没有显式定义拷贝构造函数赋值运算符,编译器会为类生成默认的拷贝构造函数赋值运算符。此外,我们还可以使用`=default`来显式要求编译器生成合成的拷贝构造函数赋值运算符。这将使用默认的实现来完成拷贝和赋值操作。 总之,拷贝构造函数用于创建一个对象的副本,而赋值运算符用于将一个对象的值赋给另一个已经存在的对象。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [C++拷贝构造函数与拷贝赋值运算符](https://blog.csdn.net/xiongya8888/article/details/89424224)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值