Default Constructor与条款4:确定对象被使用前已先被初始化
《深度探索c++对象模型》2.1 Default Constructor 的构造操作核心在于 default constructors … 在需要的时候被编译器产生出来这句话,其中暗含编译器的默认构造函数并不“可靠”,由此《Effectice C++》认为每个class最好拥有显示的构造函数。
随之带来的问题是,如何写好一个高效的构造函数?更具体的,如何配合使用编译器默认构造函数,使构造函数不至于非常庞杂,同时不产生过多的临时对象?这就需要理解《深度搜索c++对象模型》中4个nontrival的构造函数,在这4种情况下,编译器会辅助自动生成一些代码:
- “带有default constructor”的member class object
- “带有default constructor”的base class object
- "带有virtual function"的class
- “带有virtual Base class”的class
3、4条主要关乎虚表相关概念,在了解虚函数、虚基类使用的虚表技术后,便豁然开朗,在此先不作说明。本文重点将放在1、2条,以书上的一个例子进行阐述。
class Dopey {public: Dopey();};
class Sneezy {public:Sneezy(int);Sneezy();...};
class Bashful{public: Bashful();..};
class Snow_White
{
public:
Dopey dopey_;
Sneezy sneezy_;
Bashful bashful_;
private:
int numble_;
string name_;
}
基于第一条原则,若没有显示书写构造函数,编译器将为其自动生成如下的默认构造函数:
Snow_White::Snow_White()
{
dopey_.Deopy::Doepy();
sneezy_.Sneezy::Sneezy();
bashful_.Bashful::Bashful();
name_.string::string();
}
推荐的显示构造函数写法:
Snow_White::Snow_White(int numble,string name):sneezy_(1024),numble_(numble),name_(name)
{}
//将被编译器扩展成:
Snow_White::Snow_White(int numble,string name)
{
dopey_.Deopy::Doepy();
sneezy_.Sneezy::Sneezy(1024);
bashful_.Bashful::Bashful();
numble_ = numble;
name_.string::string(name);
}
不推荐的写法如下,在《Effective c++》中认为这种写法属于assign而非构造,《深度探索c++对象模型》则表明使用该种写法,实际是在调用默认拷贝构造函数,而通常情况下默认拷贝构造函数将额外产生一个临时对象,函数结束还需进行销毁,所以该写法是低效的。
Snow_White::Snow_White(int numble,string name):sneezy_(1024)
{
numble_ = numble;
name_ = name;
}
同时值得注意的是,在采用上述推荐写法时的赋值顺序,最好按照定义的先后顺序进行赋值——因为编译器会强行根据定义顺序改变赋值顺序,确保自身逻辑和编译器吻合。
class Snow_White
{
public:
Dopey dopey_;
Sneezy sneezy_;
Bashful bashful_;
private:
int numble_;
string name_;
string name2_;
}
//这种写法编译器会自动改变
Snow_White::Snow_White(int numble,string name,string name2):sneezy_(1024),numble_(numble),name2_(name2),name(name_)
{}