C++编程语言中类对象的赋值与复制介绍(一)

本系列文章主要介绍C++编程语言中类对象的赋值和复制操作,以及两者之间的区别,另外还会介绍“深拷贝”与“浅拷贝”的相关知识。

本文为系列文章的第一篇,主要介绍C++编程语言中类对象的赋值的相关知识。

1 对象的赋值

1.1 What

如同基本类型的赋值语句一样,同一个类的各个对象之间也是可以进行赋值操作的,即,将一个对象的值赋给另一个对象。

需要说明的是,在类对象的赋值过程中,只会对类中的数据成员进行赋值,而不对成员函数赋值。

例如,obj1和obj2是类ClassA的两个对象,那么对象赋值语句“obj2 = obj1;” 会把对象obj1的数据成员的值逐位赋给对象obj2。

1.2 示例代码

此处展示一个对象赋值的示例代码(object_assign_and_copy_test1.cpp),内容如下:

#include <iostream>

using namespace std;

class ClassA
{
public:
    // 设置成员变量的值
    void SetValue(int i, int j)
    {
        m_nValue1 = i;
        m_nValue2 = j;
    }
    // 打印成员变量的值
    void ShowValue()
    {
        cout << "m_nValue1 is: " << m_nValue1 << ", m_nValue2 is: " << m_nValue2 << endl;
    }
private:
    int m_nValue1;
    int m_nValue2;
};

int main()
{
    // 声明对象obj1和obj2
    ClassA obj1;
    ClassA obj2;

    obj1.SetValue(1, 2);
    // 对象赋值场景 —— 将obj1的值赋给obj2
    obj2 = obj1;
    cout << "obj1 info as followed: " << endl;
    obj1.ShowValue();
    cout << "obj2 info as followed: " << endl;
    obj2.ShowValue();

    return 0;
}

编译并运行上述代码,结果如下:

上面的运行结果表明,通过对象赋值语句,成功地将对象obj1的值赋给了对象obj2。

1.3 几点说明

对于对象赋值,进行以下几点说明:

  • 进行对象赋值时,两个对象的必须属于同一个类,如对象所属的类不同,在编译时会报错;
  • 两个对象之间的赋值,只会让这两个对象中数据成员相同,而两个对象仍然是独立的。例如,在上面的示例代码中,进行对象赋值后,再调用obj1.set()设置obj1的值,并不会影响到obj2的值;
  • 对象赋值是通过赋值运算函数实现的。每个类都有默认的赋值运算符,我们也可以根据需要,对赋值运算符进行重载。一般来说,需要手动编写析构函数的类,都需要重载赋值运算符(具体原因下文会介绍);
  • 在对象声明之后,进行的对象赋值运算,才属于“真正的”对象赋值运算(即,使用了赋值运算符“=”);而在对象初始化时,针对对象进行的赋值操作,实际上是属于对象的复制。示例如下:
        // 声明obj1和obj2
        ClassA obj1;
        ClassA obj2;
        obj2 = obj1; // 此语句为对象的赋值
    
        // 声明obj1
        ClassA obj1;
        // 声明并初始化obj2
        ClassA obj2 = obj1; // 此语句属于对象的复制

1.4 进一步研究

下面从内存分配的角度分析一下对象的赋值操作。

1.4.1 C++中对象的内存分配方式

在C++编程语言中,只要声明了对象,对象实例在编译的时候,系统就需要为其分配内存了。

相关示例代码如下:

class ClassA
{
public:
    ClassA(int id, char* name)
    {
        m_nId = id;
        m_pszName = new char[strlen(name) + 1];
        strcpy(m_pszName, name);
    }
private:
    char* m_pszName;
    int m_nId;
};

int main()
{
    ClassA obj1(1, "liitdar");
    ClassA obj2;

    return 0;
}

在上述示例代码编译之后,系统为对象obj1和obj2都分配对应大小的内存空间(只不过对象obj1的内存域被初始化了,而obj2的内存域的值为随机值)。两者的内存空间使用情况如下:

1.4.2 默认的赋值运算符

延续上面的示例代码,执行“obj2 = obj1;”语句时,即利用默认的赋值运算符将对象obj1的值赋给obj2。使用类中默认的赋值运算符,会将对象中的所有位于stack中的域进行相应的复制操作;同时,如果对象有位于heap上的域,则不会为目标对象分配heap上的空间,而只是让目标对象指向源对象heap上的同一个地址。

执行语句“obj2 = obj1;”对应的默认的赋值运算后,两个对象的内存空间使用情况如下:

因此,对于类中默认的赋值运算,如果源对象域内没有heap上的空间,则不会产生任何问题。但是,如果源对象域内需要申请heap上的空间,那么由于源对象和目标对象都指向heap的同一段内容,所以在析构对象的时候,就会连续两次释放heap上的同一块内存区域,从而导致程序异常。

    ~ClassA()
    {
        delete m_pszName;
    }

1.4.3 解决方案

为了解决上面的问题,如果对象会在heap上存储内容,就必须重载赋值运算符,从而在进行对象的赋值操作时,使不同对象的成员域指向不同的heap地址。

重载赋值运算符的代码如下:

    // 赋值运算符重载需要返回对象的引用,否则返回后其值立即消失
    ClassA& operator=(ClassA& obj)
    {
        // 释放heap内存
        if (m_pszName != NULL)
        {
            delete m_pszName;
        }
        // 赋值stack内存的值
        this->m_nId = obj.m_nId;
        // 赋值heap内存的值
        int nLength = strlen(obj.m_pszName);
        m_pszName = new char[nLength + 1];
        strcpy(m_pszName, obj.m_pszName);
        
        return *this;
    }

使用上面重载后的赋值运算符对对象进行赋值后,两个对象的内存空间使用情况如下:

这样,在对象obj1、obj2退出其的作用域,调用相应的析构函数时,就会释放不同heap空间中的内存了。

关于本系列文章的第二篇,“C++编程语言类对象的复制操作”的相关内容,请点击此处

  • 49
    点赞
  • 189
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

liitdar

赠人玫瑰,手有余香,君与吾共勉

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

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

打赏作者

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

抵扣说明:

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

余额充值