只此一文、道破C++赋值运算符重载玄机(operator)

赋值运算符重载是C++重点难点内容,什么是赋值运算符?什么是赋值运算符重载?两者有什么联系?怎么学习?怎么理解?怎么应用?初学者往往容易一头雾水,作者总结这篇文章就是为了解决C++初学者的疑惑。

  • 赋值运算符

我的理解:赋值运算符和变量赋值一样,变量之间可以赋值,那么对象也应该具有这样的特性。赋值运算符(=)可以用来将一个对象拷贝给另一个已经存在的对象。

注意:对象之间的赋值是将成员变量依次拷贝,而不是将整个对象的内存按位拷贝。

#include<iostream>
using namespace std;
class Stud     //定义一个学生类,包含学生的年龄
{
private:
    int arg;
public:
    Stud()
    {};
    Stud(int _arg)
        :arg(_arg)
    {
        std::cout << "arg   "<< arg << endl;
    }
    Stud&operator=(const int _arg)         //赋值运算符重载
    {
        cout << "operator=(int _arg)" << endl;
        arg = _arg;
        return *this;
    }
};

int main()
{
    Stud(5);
    Stud xiaoming, litao;
    xiaoming = 55;
    litao = xiaoming;     //对象之间的赋值
    xiaoming;
    litao;
    return 0;
}

一般情况下,赋值运算符就能搞定对象之间的赋值,但是当一个类中包含指针类型的成员变量时,可能会带来问题,请看下面的代码:

#include <iostream>
using namespace std;
class Book{      //定义一本书,包含价格、书签(页码)、书签数量
private:
    double m_price;     //书的价格
    int *m_bookmark;     //书签
    int m_num;       //书签的数量
public:
    Book() : m_price(0.0), m_bookmark(NULL), m_num(0){}
    Book(double price, int *bookmark, int num);
    void setBookmark(int, int);     //setBookmark() 函数用来修改某个书签
    void display();      //打印
};

Book::Book(double price, int *bookmark, int num) : m_price(price), m_num(num)
{
    int *bmTemp = new int[num];
    for (int i = 0; i<num; i++)
    {
        bmTemp[i] = bookmark[i];
    }
    this->m_bookmark = bmTemp;
}

void Book::setBookmark(int page, int index)   //书签可以有多个,所以需要将它放在数组中
{
    if (index >= m_num - 1)
    {
        cout << "Out of bound!" << endl;
    }
    else
    {
        m_bookmark[index] = page;
    }
}

void Book::display()
{
    cout << "price: " << m_price << endl;
    cout << "bookmarks: ";
    for (int i = 0; i<m_num; i++)
    {
        if (i == m_num - 1)
        {
            cout << m_bookmark[i] << endl;
        }
        else
        {
            cout << m_bookmark[i] << ", ";
        }
    }
}

int main(){
    int m_bookmark[] = { 1, 9, 600, 250 };
    Book java, cpp(68.5, m_bookmark, 4);
    cpp.display();
    java = cpp;       //对象之间赋值
    java.setBookmark(100, 2);
    cpp.display();
    return 0;
}

运行结果截图:
这里写图片描述

一语道破玄机(总结):

1> Book 类的构造函数中不是直接接收参数的值,而是根据参数所指向的数组,再创建一个新的数组。这样做的好处是对象有属于自己的数组,在其他地方修改实参所指向的数组不会影响该数组,能够很好的隔离。
2> 在main函数中,两次调用 display() 的结果不同表明,调用 java 对象的 setBookmark( ) 函数影响到了 cpp 对象。这是因为,执行java = cpp;语句时会将 cpp.m_bookmark 的值复制给 java.m_bookmark,不同对象的成员变量指向同一个数组,当然会相互影响。


要解决这个问题,就需要重载赋值运算符,如下所示:

Book & Book::operator=(const Book &b){
    if (this != &b){
        this->m_price = b.m_price;
        this->m_num = b.m_num;
        //为bookmark赋值
        int *bmTemp = new int[b.m_num];
        for (int i = 0; i<b.m_num; i++){
            bmTemp[i] = b.m_bookmark[i];
        }
        this->m_bookmark = bmTemp;
    }
    return *this;
}

将这个函数放入 Book 类中,再执行java = cpp;语句时,会转换为:

java.operator=(cpp);

在函数体中,this 就指向 java 对象。这样 java 对象也会拥有属于自己的数组,两个对象之间不会再相会影响。
可以发现,重载赋值运算符时,函数的参数和返回值类型都必须是对象的引用。以 Book 类为例来说,赋值运算符重载函数一般有两种原型:

Book & operator=( Book &b );
Book & operator=( const Book &b );

返回值和参数都是 Book 类对象的引用。下面一种原型则规定在赋值时不能修改原来的对象。
赋值运算符重载函数除了能有对象引用这样的参数之外,也能有其它参数。但是其它参数必须给出默认值。如下所示:

Book & operator=(const Book &b, a = 20);

猜你喜欢

转载自blog.csdn.net/m0_37925202/article/details/78735149