跳至主要內容

赋值运算符函数

张威大约 3 分钟c/c++类和对象

赋值运算符函数

img
img

形式

赋值运算是一种很常见的运算,比如:

int x = 1, y = 2;
x = y;

同样地, 我们也希望该操作能作用于自定义类类型,比如:

Point pt1(1, 2), pt2(3, 4);
pt1 = pt2;//赋值操作

在执行pt1 = pt2;该语句时,pt1与pt2都存在,所以不存在对象的构造,这要与 Point pt2 = pt1;语句区分开,这是不同的。

Point pt1(1, 2);
/* Point pt4 = pt1;//拷贝构造函数 */
Point pt4(pt1);//拷贝构造函数

Point pt2(3, 4);
pt2 = pt1;//赋值运算符函数

在这里,=作用于对象时,其实是把它当成一个函数来看待的。在执行pt1 = pt2;该语句时,需要调用的是赋值运算符函数。其形式如下:

返回类型 类名::operator=(参数列表)
{
    //...
}

如果类中没有显式定义赋值运算符函数时,编译器会自动提供一个缺省的赋值运算符函数。

Point &Point::operator=(const Point &rhs)
{
    _ix = rhs._ix;
    _iy = rhs._iy;
    
    return *this;
}

深拷贝和浅拷贝问题

class Computer
{
public:
    Computer(const char *brand, double price)
    : _brand(new char[strlen(brand) + 1]())
    , _price(price)
    {
         cout << "Computer(const char *, double)" << endl;
    }
    
    Computer &operator=(const Computer &rhs)
	{
    	_brand = rhs._brand;	//浅拷贝
    	_price = rhs._price;
    
    	return *this;
	}
    
    Computer &Computer::operator=(const Computer &rhs)
	{   
    	if(this != &rhs) //1、自复制
    	{
        	delete [] _brand; //2、释放左操作数
        	_brand = nullptr;
        
        	_brand = new char[strlen(rhs._brand) + 1](); //3、深拷贝
        	strcpy(_brand, rhs._brand);
        
        	_price = rhs._price;
    	}
        return *this; //4、返回*this
	}
    
private:
    char *_brand;	//指向堆内存的指针
    double _price;
};

//执行构造初始化
Computer pc1("Huawei Matebook14", 5699);
Computer pc2 = pc1;

Q:引用符号可以去掉吗?

如果去掉,会多执行一次拷贝构造函数,效率就会降低

Q:const可以去掉吗:

如果右操作数是右值的时候,就会产生非const左值引用不能绑定到右值的报错

Q:赋值运算符函数的返回类型可以是void?

考虑连等情况

空类

#include <iostream>

using std::cout;
using std::endl;

class Empty
{
//无参构造函数
//拷贝构造函数
//赋值运算符函数
//析构函数
};
int main(int argc, char **argv)
{
    cout << "sizeof(Empty) = " << sizeof(Empty) << endl;

    Empty e1;
    Empty e2;
    Empty e3;
    printf("&e1 = %p\n", &e1);
    printf("&e2 = %p\n", &e2);
    printf("&e3 = %p\n", &e3);
    return 0;
}
$./a.out 
sizeof(Empty) = 1
&e1 = 0x7ffe6ef931ef
&e2 = 0x7ffe6ef931ee
&e3 = 0x7ffe6ef931ed

一个空的类,编译器会自动提供哪些函数

  1. 无参构造函数
  2. 拷贝构造函数
  3. 赋值运算符函数
  4. 析构函数

空类的大小不为0

C++标准指出,不允许一个对象(当然包括类对象)的大小为0,。这是由于:

  • new需要分配不同的内存地址,不能分配内存大小为0的空间
  • 避免除以 sizeof(T)时得到除以0错误

故使用****来区分空类。

值得注意的是,这并不代表一个空的基类也需要加一个字节到子类中去。这种情况下,空类并不是独立的,它附属于子类。子类继承空类后,子类如果有自己的数据成员,而空基类的一个字节并不会加到子类中去。例如,

class Empty {};
struct D : public Empty {int a;};	//sizeof(D)为4

再来看另一种情况,一个类包含一个空类对象数据成员。

class Empty {};
class HoldsAnInt {
    int x;
    Empty e;
};

在大多数编译器中,你会发现 sizeof(HoldsAnInt) 输出为8。这是由于,Empty类的大小虽然为1,然而为了内存对齐,编译器会为HoldsAnInt额外加上一些字节,使得HoldsAnInt被放大到足够又可以存放一个int