这是对Effective C++这本书中的部分内容进行的总结以及代码实践,主要是记录一些对我印象深刻的,确实能改善程序的内容,和有需要实践验证加深印象的一部分实践和我自己的理解
当我们创建了一个类后,即使这个类中并没有写任何成员函数,但是编译器依旧会为我们生成六个成员函数,也叫做六大默认成员函数
这六个成员函数分别为:
其中需要注意的是,编译器默认生成的构造函数是一个浅拷贝,只是把非静态成员进行值拷贝,关于浅拷贝和深拷贝前文有讲述,因此完全使用编译器生成的构造函数有时是不够的,需要我们自己完善编写
这里对静态成员变量也进行一下补充:
因此编译器默认生成的拷贝构造函数只对non-static
成员变量奏效
关于构造函数的建议:
编译器默认生成的构造函数是无参的版本,而在实际应用中常常需要用到带参数的构造函数,因此这里对于一个类来说,自己写一个全缺省的构造函数会便利很多
关于赋值操作符重载:
赋值重载函数也是默认成员函数,但编译器是可以拒绝的,只有当生出的代码合法,并且有适当机会证明有意义编译器才会生成
#include
#include
using namespace std;
template <class T>
class nameobject
{
public:
nameobject(string& name, const T& value)
:namevalue(name)
,objectvalue(value)
{}
private:
string& namevalue;
const T objectvalue;
};
int main()
{
string newdog("Tom");
string olddog("Jim");
nameobject<int> a(newdog,1);
nameobject<int> b(olddog,2);
a = b;
return 0;
}
对于上面的代码,编译器无法解析a=b
究竟该如何解释,如果解释为将b
中元素赋予给a
,那么就违背了引用不能指向不同对象这一原则,编译器不知该如何进行操作,因此拒绝生成赋值重载函数,此时需要我们自己来完成这一函数
编译器可以暗自为class创建default构造函数、copy构造函数、copy assignment操作符,以及析构函数
编译器默认会生成成员函数,但假设现在有这样的情景,我不允许外部使用类的时候调用拷贝构造和赋值重载函数,但如果我在类内不写编译器也会默认生成,此时应该如何解决?
解决方法是,将函数写到private类内
外部成员是不可以调用私有类成员的,因此将函数写到私有就可以解决这个问题
根据这个原理,可以考察一个有趣的面试题
如何定义一个只能在堆上或栈上生成对象的类?
为驳回编译器自动提供的机能,可将相应的成员函数声明为private并且不予实现
这个条款比较简单,令赋值操作符返回一个reference to *this
这只是一个协议,并没有强制性,如果你不遵循它也不会报错,但是这样可以提高效率,同时也算是一个共识,我们最好随众
赋值重载函数并没有想象那么简单,其中有很多小细节
如果有下面的代码:
#include
#include
using namespace std;
class B
{
public:
B(int a=0,int b=1)
:_a(a)
,_b(b)
{}
private:
int _a;
int _b;
};
class A
{
public:
A()
{
tmp = new B;
}
A& operator=(const A& p)
{
delete tmp;
tmp = new B(*p.tmp);
return *this;
}
private:
B* tmp;
};
int main()
{
A a1;
A a2;
a1 = a2;
return 0;
}
现在来看是没有问题的,很正常的完成了赋值的操作,但事实上是有问题的
如果这里赋值重载函数中的*this
和形参是同一个对象,那么此时在执行代码的时候就会销毁掉这个对象的内容,同时赋值的结果就会导致拥有一个被删除的对象,这是错误的行为
解决方案1
因此可以在函数前加一个if
语句判断条件,如果形参和this指针
相同,就直接返回this指针
,这是一种解法
解决方案2
A& operator=(const A& p)
{
B* cur = tmp;
tmp = new B(*p.tmp);
delete tmp;
return *this;
}
交换一下语句的顺序,在复制前做到不要删除原来的数据即可避免这样的情况发生
解决方案3
但最好的方法,是利用所谓的copy-and-swap
方法:
A& operator=(const A& p)
{
A tmp(p);
swap(tmp);
return *this;
}
这相当于是一个很巧妙的方法,利用形参在类内直接构造出一个对象,再将这个对象和*this
进行交换,则此时就把形参的数据很巧妙的拷贝到了this指针
中,完成了赋值的目的,同时也解决了前面出现的问题
确保当对象自我赋值时operator=有良好的行为,其中技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap
确认任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确
当类内的私有成员增添了某些成员后,如果构造函数为自己构建,要补上对应的构造函数,编译器不会对此做出报错
Copying函数应该确保复制“对象内的所有成员变量”及“所有base class成分”
不要尝试以某个copying函数实现另一个copying函数,如果有重叠部分可以通过第三个函数调用