类最基本的四个函数:构造函数、复制构造函数(也叫拷贝构造函数-copy constructor)、赋值函数和析构函数的其中一个如果没有被我们显示编写,则编译器会为我们生成一个合成的。
但这个合成的只负责一些简单的功能,比如构造函数。
一,默认构造函数
一个设计良好的类应该有默认构造函数,当然,有的时候就用编译器给我们的合成的也能满足我们的需求。但是有一个独立编写的默认构造函数总是好的,下面我就简单地介绍其必要性。
1,为动态分配数组元素的时候
没有默认构造函数的类不能用作动态分配数组的元素类型。
Type t = new Type[10];
很显然,上面的代码要求Type类型有自己的默认构造函数,不然编译无法初始化动态分配的10个元素。
2,为静态数组元素类型的时候
虽然没有默认构造函数的类型不能动态分配为数组元素,但是静态分配是可以的。
Type T("Tom"); //假设Type类型的构造函数接受string类型的参数。
在分配数组的时候就要大量的浪费精力去显示初始化,
Type T[100] = { "Tom", "John", "Han", "Josh"......}; //繁琐
3,作为容器元素类型的时候
容器元素的类型必须有初始化值,如果要把没有默认构造函数的类型Type作为容器vector的元素,那么就要为每一个元素显示初始化。很显然,又是一项繁琐又让人繁忙的工作。
vector<Type> vt; //错误,必须显示初始化
二,隐式转换
看如下代码:
class Player {
public:
Player(std::string name = ""): m_name(name) {}
bool IsSameName(std::string name) {return m_name == name;}
private:
std::string m_name;
int m_ID;
}
Player有一个接受string类型的构造函数和一个接受string类型的IsSameName方法,假如有以下调用:
int main()
{
Player John;
John.IsSameName("Tom");
}
调用John对象里的IsSameName方法来判断是否对象的名字和"Tom“相同。但是因为Player有一个可以接受string类型的构造函数,所以John对象在调用IsSameName方法的时候会隐式地生成一个将"Tom"作为构造函数的参数的临时对象,函数调用完毕以后释放它。
当然我们这个实例类出现这个问题并不会造成太大的错误,但是要是一个对象有一个接受流对象的构造函数呢?或者其他更危险的动作?
那样稍有不慎便会造成整个程序甚至系统崩溃。
所以更有效的定义构造函数应该在前面加上explicit关键字,如下:
explicit Player(std::string name): m_name(name) {}
这样就可以防止对象在我们不需要的情况下悄悄地隐式转换了。
转自:http://blog.chinaunix.net/u2/85205/showart_1408651.html
NoDefault为一个没有默认构造函数的类
NoDefault类型不能用作动态分配数组的元素类型
NoDefault类型的静态分配数组必须为每个元素提供一个显式的初始化式、
原因:对于动态分配的数组,其元素只能初始化为元素类型的默认值,而对于静态数组,可以用初始化列表为数组元素提供不相同的初值,初始化列表中调用类的带参构造函数为数组中的对象赋初值。
如果有一个保存NoDefault对象的容器,例如vector,就不能使用接受容器大小而没有同时提供一个元素初始化式的构造函数。
原因:例如,定义一个vector对象
vector<NoDefault> ivec(2); //确定元素的个数,并用默认构造函数给每个元素初始化
由于NoDefault没有默认构造函数,因此将不能自动初始化ivec中的元素。因此,应该在定义中加上元素的初始值
例如
vector<NoDefault> ivec(2,"a","b");
文章出处:http://www.diybl.com/course/3_program/c++/cppjs/2008311/104151.html