作者:冯利美,华清远见嵌入式学院讲师。
构造函数,拷贝构造函数,赋值函数和析构函数是C++类中最基本的四大函数。当设计一个类时,要首先考虑这四大函数的写法。若没有提供显式的实现,编译器会产生默认的函数。若类中有指针成员,必须提供这四大函数的实现,否则容易出现内存错误。本文针对赋值函数进行了分析,包括其原型,调用场合,存在的必要性等方面。
1. 赋值函数原型
A& operator =( const A& other) { … … }
2. 调用场合
A a1(10); // 为a1调用构造函数
A a2; // 为a2调用默认构造函数
a2 = a1; // 为a2调用赋值函数。
A & operator =(const A &a){
if (&a == this)
return *this;
//... 具体赋值操作
return *this;}
3.考察:
1) 为何首先检查同一性?
答:为了防止自赋值
2) (a=b)=c或者a=(b=c)是否合法
答:合法
3) 若定义为void operator =(const A &a) 有何局限?
答:没有了返回值,就不能实现a=b=c 这样的链式复值。用法不够灵活。
4) 赋值函数存在的必要性
答:以类String的两个对象a,b为例,假设a.m_data的内容为“hello”,b.m_data的内容为“world”。现将a赋给b,缺省赋值函数的“位拷贝”意味着执行b.m_data = a.m_data。这将造成三个错误:一是b.m_data原有的内存没被释放,造成内存泄露;二是b.m_data和a.m_data指向同一块内存,a或b任何一方变动都会影响另一方;三是在对象被析构时,m_data被释放了两次。
5) 若定义为A operator =(const A &a){...return *this;},有何局限?
答:若返回值改成了不是引用类型,则有两个局限。
第一:对于a=(b=c), 操作仍然可以正常进行,但效率降低了。
因为此时的赋值函数会产生一个临时对象,类似于 A tmp=*this. 假设b=c操作产生tmp1, 然后执行a=tmp1,该过程还会产生临时对象tmp2。还会有tmp1, tmp2的析构。过程变得复杂许多。
第二:对于(a=b)=c, 操作不能以期望的方式进行。
假设a=b产生临时对象tmp1, 然后tmp1=c, 这样a不能获得c的值,与常识不符。