C++中构造函数是特殊的成员函数,只要定义类的新对象就会执行构造函数,构造函数是保证每个对象的数据成员具有合适的初始值。
构造函数特点:
1)构造函数与类同名,不能指定返回类型,且不能声明为const函数,详见你所不知道的 const
2)构造函数可以重载,所以可以有多个构造函数
sales_item(void) /*const*/;
sales_item(istream &is /*= cin */);
sales_item(const string & book ); //must const
sales_item(const sales_item &org);
3)调用构造函数,使用不同的实参就会调用不同的构造函数,如下
sales_item sales0;
sales_item sales1("hello world");
sales_item("hello world"); //也可以
sales_item sales2 = sales_item("123456"); //定义
sales_item sales3(cin);
构造函数的初始化式:
函数后面可以包含一个初始化列表,原型为形参后紧跟一个冒号,然后就是以逗号分开的数据成员列表,初始化值放在圆括号内,如下
sales_item::sales_item(void)
: isbn(1,'0'), units_sold(0), revenue(0.0), a(0), b(a){}
初始化注意之处:
1)构造函数分两阶段进行,初始化阶段和普通的计算阶段(计算阶段就是函数体中的语句或者称为赋值阶段)
2)未提供显式初始化的变量使用与初始化变量相同的规则进行初始化,内置或者复合类型依赖于对象的作用域,局部不初始化全局初始化为0,类类型使用默认的构造函数
3)必须提供初始化列表的情况,如下
screen::screen(void)//contents由string默认构造函数初始化
: nConstVar(0)/*contents("")*/,cursor(0),height(0),width(0),access_ctr(0){}
const int nConstVar; //必须在直接初始化列表进行初始化,如果在函数体中就会出错,还有就是引用的初始化,如下sales_item::sales_item(void)
: isbn(1,'0'), units_sold(0), revenue(0.0), a(0), b(a){}
int &b; b一定要显式初始化
4)成员初始化的次序
class x {
int i;
int j;
public:
x(int val):j(val), i(j){};
};
这里我在vs2012编译下通过了,显式初始化次序对这个没有影响,在gcc version 4.4.1使用g++编译同样通过。但是在vs2012调试后发现,i=-858993460,j=1 也就是说其实i并没有提供的j的值初始化,所以最好严格按照初始化顺序初始化,尽可能避免使用成员来初始化,最好统一使用形参来进行初始化。5)类类型的初始化可以使用该类型的任意构造函数,也可以使用默认的构造函数。
6)类成员的显示初始化,直接初始化成员也是可以的,但是必须满足一下几点
a. 类的全体成员必须是public
b. 没有定义构造函数
如下:
struct init
{
int ival;
char * ptr;
};
init initval = {1, NULL};
默认实参:
1)可以为构造函数提供默认的实参,但是这个形参后面的所有形参必须提供默认值,所以需要合理调整形参的位置。
2)另外默认值只能提供在类声明的位置处,不能在定义处提供,否则编译会出错。如下
class y
{
public:
y(ifstream &ifs, const string &str = ""):strVal(str), nVal(0), dVal(NULL), ifsVal(ifs){};
~y(){};
private:
const string strVal;
int nVal;
double* dVal;
ifstream& ifsVal;
};
3)可以有默认值,但是提供默认值后有些需要合并的函数就需要删除,不能出现调用时出现歧义,如下
sales_item(void) /*const*/;
~sales_item(void);
sales_item(istream &is = cin );
sales_item(const string & book = "" ); //must const
这里就会发现,如果调用sale_item默认不带形参的构造函数就会出现歧义,不知该调用哪个
默认构造函数:
1)合成的默认构造函数,一个类哪怕只定义一个构造函数,编译器就不会生成合成默认构造函数
2)合成的默认构造函数使用与变量初始化相同的规则来初始化成员,如果类包含内置类型或者复合类型的成员,则该类不应该依赖于合成的默认构造函数。
3)类通常应该定义一个默认构造函数,只要定义了其他构造函数,提供默认构造函数总是对的
4)使用默认构造函数时,切记不可写成如下的形式
sales_item item();
这种写法是错误的,因为这里其实声明的item是函数,返回类型是sales_item,但是下面是允许的
sales_item item1 = sales_item(); //定义
或者
sales_item* item2 = new sales_item();
隐式转转:
当调用构造函数的时候,如果对形参没有特别说明,就有可能发生隐式转换,如下
const string book = "9-99-9999";
item.same_ishn(book);
这里就将string转换为sales_item类类型,但是有时候却需要抑制这种转化,如下
explicit sales_item(void); // 防止隐式转换