cpp运算符重载-赋值重载

05-4 运算符重载-赋值重载

在学习类中的构造函数和析构函数的时候,我们学习过:写了一个类,c++的编译器会给这个类自动添加四个函数。

  1. 默认构造函数(无参,函数体为空)
  2. 默认析构函数(无参,函数体为空)
  3. 默认拷贝构造函数(对属性进行值拷贝)
  4. 赋值运算符operator=,用于对属性进行值拷贝

在学习深拷贝和浅拷贝的时候我们也提到过:如果类中有属性是指向堆区的指针,那么则有可能出现深拷贝和浅拷贝的问题。

我们可以举个例子:

#include <iostream>
using namespace std;
ostream& operator<<(ostream& cout, class test& p);
class test
{
    friend ostream& operator<<(ostream& cout, class test& p);
private:
    int* p_m_A;
    int m_B;
public:
    test()
    {
        p_m_A = new int(10);
        m_B = 20;
    }
    test(int A, int B)
    {
        p_m_A = new int(A);
        m_B = B;
    }
};
ostream& operator<<(ostream& cout, class test& p)
{
    cout << "p_m_A = " << (int)p.p_m_A << " * p_m_A = " << *p.p_m_A << " m_B = " << p.m_B ;
    return cout;
}
void test1()
{
  class test t1;
    cout << "t1" << t1 << endl;
    class test t2(20, 30);
    cout << "t2" << t2 << endl;
    t2 = t1;
    cout << "t2" << t2 << endl;
}
int main()
{
    test1();
    return 0;
}

运行结果:(环境:Windows11(arm/Apple M VM)/Visual Studio 2022/Debug/arm64)

t1p_m_A = 446792128 * p_m_A = 10 m_B = 20
t2p_m_A = 446792256 * p_m_A = 20 m_B = 30
t2p_m_A = 446792128 * p_m_A = 10 m_B = 20

在这个类test中,有两个属性,一个属性是p_m_A(这是一个指针),另外一个是m_B

我们先创建t1,然后将t1使用赋值的方式赋值给t2,因为默认的赋值是值拷贝,会把t1.p_m_At1.m_B的数据值一模一样赋值给t2。这时候,t1t2两个对象中的p_m_A的数值完全一样。但是,因为p_m_A是一个地址,所以访问的实际数据都是同一个,意味着t1t2无论哪个对象被删除都会造成非法访问。

如果我们加上一段析构函数:

~test()
{
    if (p_m_A != NULL)
    {
        delete p_m_A;
        p_m_A = NULL;
    }
}

再次运行程序,程序会当场崩溃。

这是因为,我们创建t1的时候,数据会在堆区开辟一块内存用于存放数据,p_m_A会记录存放内存数据的地址。当我们使用默认提供的赋值时,因为是值拷贝,会完完全全的拷贝t1.p_m_A上的数据(此时,这上面的数据是地址)到t2.p_m_A

当析构代码被执行的时候,t2被释放了,析构函数中会检测t2.p_m_A是否为空。因为不为空,所以会释放一次。之后再次释放t1的时候,再次检查t1.p_m_A是否为空,则会再次释放这块内存。导致一个内存被两次释放。

解决方法:使用赋值运算符重载,进行深拷贝。将类中出现的地址使用new操作符新申请一块内存空间存放。

class test& operator=(class test& p)
{
    if (p_m_A != NULL)
    {
        delete p_m_A;
        p_m_A = NULL;
    }
    p_m_A = new int(*p.p_m_A);
    m_B = p.m_B;
    return *this;
}

请注意,赋值运算符重载只可以在成员函数中重载。

  • 思路:
  1. 检测被赋值的对象中是否有指向堆区的数据,如果有,需要删除。
  2. 将需要赋值的原数据中指针的变量先解引用,然后使用new创建出一块新的内存存储
  3. 为了实现链式编程,返回值需要返回自己。在类中的成员函数返回自己需要使用*this
  • 注意:
    1. 如果类中有属性是指向堆区的指针,那么则有可能出现深拷贝和浅拷贝的问题。
    2. 赋值运算符重载只可以在成员函数中重载。
    3. 如果类中没有指向堆区的地址,可以使用编译器提供的默认赋值。(默认提供值拷贝)。
文章「cpp运算符重载-赋值重载」,由本站用户「Admin」发布。文章仅代表Admin观点,不代表本站立场。
页面网页地址「https://xiaozhiyuqwq.top/p/915」。
如您对文章及其附件提出版权主张,或进行引用转载等,请查看我们的【版权声明】
无评论:-)

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇