赋值操作符重载
上篇文章着重讲述了复制构造函数的重要性,以及如果没有实现复制构造函数可能出现的致命错误,同时也指出赋值操作符重载在类中也和赋值构造函数一样重要.
但赋值操作符重载有着其独特的性质,本篇文章就专门介绍赋值操作符重载的一些性质,以及在实际开发中需要注意的地方.
引出问题
为了描述问题,我们简单设计一个类People, 包含三个成员m_pName, m_nAge, m_nGender,其中m_pName被声明为指向char类型的指针,用于保存姓名字符串.类头文件和cpp实现文件如下:
People.h
#ifndef PEOPLE_H
#define PEOPLE_H
class People
{
public:
People();
People(const char *name, int age, int gender);
//People(const People &pp);
virtual ~People();
People &operator=(const People &pp);
void Print() const;
protected:
private:
char *m_pName;
int m_nAge;
int m_nGender;
};
#endif // PEOPLE_H
People.cpp
#include "People.h"
#include <string.h>
#include <iostream>
using namespace std;
People::People()
{
m_pName = new char[1];
m_pName[0] = 0;
m_nAge = 0;
m_nGender = -1;
}
People::People(const char *name, int age, int gender)
{
m_pName = new char[strlen(name)+1];
strcpy(m_pName, name);
m_nAge = age;
m_nGender = gender;
}
//People::People(const People &pp)
//{
// m_pName = new char[strlen(pp.m_pName)+1];
// strcpy(m_pName, pp.m_pName);
// m_nAge = pp.m_nAge;
// m_nGender = pp.m_nGender;
//}
People::~People()
{
delete []m_pName;
}
People &People::operator=(const People &pp)
{
if(this == &pp)
{
return *this;
}
delete []m_pName;
m_pName = new char[strlen(pp.m_pName)+1];
strcpy(m_pName, pp.m_pName);
m_nAge = pp.m_nAge;
m_nGender = pp.m_nGender;
return *this;
}
void People::Print() const
{
std::cout<<"name:"<<m_pName<<endl;
std::cout<<"age:"<<m_nAge<<endl;
std::cout<<"gender:"<<m_nGender<<endl;
}
首先,我们吧复制构造函数注释掉,然后在main中调用People类的对象:
main.cpp
#include <iostream>
using namespace std;
#include "People.h"
int main()
{
cout << "Hello world!" << endl;
{
People p("Emily", 28, 0);
p.Print();
People p1 = p;
p1.Print();
People p2;
p2 = p1;
p2.Print();
}
return 0;
}
运行main,结果输出如下图所示:
从输出结果分析,程序对m_pName释放了两次,引发异常,程序崩溃.
分析
即使实现了赋值操作符重载,当使用形如"type_name obj_dst = obj_src;"这样的赋值语句对对象赋值时,程序仍然没有调用赋值操作符,而是调用了编译器默认提供的复制构造函数.
这是因为默认的复制构造函数形参只有一个,C++中对对象赋值时,将调用只有一个参数的构造函数,而此时这个构造函数正好与复制构造函数相匹配,所以调用了复制构造函数.但是我们在本例子中,把复制构造函数注释掉了,这样,程序就调用了编译器生成的复制构造函数,而默认的复制构造函数没有深拷贝m_pName成员,所以在析构函数被执行时,对象就试图delete不属于它创建的内存.
总结
必须实现复制构造函数,否则,只能使用先声明,后赋值的语法对对象赋值,如:
People p1(“Alex”, 30, 1);
PeoPle p2;
p2 = p1;
这时,重载的赋值操作符将被调用.