构造函数是什么?
构造函数是特殊的成员函数,只要创建类类型的新对象,都要执行构造函数。构造函数的工作是保证每个对象的数据成员具有合适的初始值。
class Sales_item
{
public:
//operations on Sales_item objects
//default constructor needed to initialize members of built-in type
Sales_item():units_sold(0),revenue(0.0) {}
private:
std::string isbn;
unsigned units_sold;
double revenue;
};
isbn成员由string的默认构造函数隐式初始化为空串。
这个构造函数使用构造函数初始化列表来初始化units_sold和revenue成员。
构造函数的名字与类的名字相同,并且不能指定返回类型。向其他任何函数一样,他们可以没有形参,也可以定义多个形参。
1.构造函数可以被重载
可以为一个类声明的构造函数的数量没有限制,只要每个构造函数的形参表是唯一的,也可以理解为重载。一般而言,不同的构造函数允许用户以不同的方式来初始化数据成员。
例如,逻辑上可以通过提供两个额外的构造函数来扩展Sales_item类:一个允许用户提供isbn的初始值,另一个允许用户通过读取istream对象来初始化对象:
class Sales_item
{
//other members as before
public:
//added constructors to initialize from string and istream
Sales_item(const std::string&);
Sales_item(std::istream&);
Sales_item();
};
2.实参决定使用哪个构造函数
理解为函数的重载就好,编译器自动为你匹配相应的构造方法,但要注意,若是没有符合的构造函数就GG了。
值得注意的是,在没有构造器的定义中,所运行的是编译器所提供的默认构造函数。
3.构造函数自动执行
只要创建该类型的一个对象,编译器就运行一个构造函数:
Sales_item Primer_2nd_ed("0-201-54848-8");
Sales_item *p = new Sale_item();
第一种情况下,运行接受一个string实参的构造函数,来初始化变量Primer_2nd_ed。第二种情况下,动态分配一个新的Sales_item对象。假定分配成功,则通过默认构造函数初始化该对象。
4.用于const对象的构造函数
构造函数不能被声明为const:
class Sales_item
{
public:
Sales_item() const; //error
};
const构造函数是不必要的。创建类类型的condt对象时,运行一个普通构造函数来初始化该const对象。构造函数的工作是初始化对象。不管对象是否为const,都用一个构造函数来初始化该对象。
解释:
-
const修饰函数表示该函数的返回值是const类型的,改返回值只能赋值给同类型的const变量。
-
const是可以修饰类的成员函数,但是该函数不能修改数据成员。构造函数也属于类的成员函数,但是构造函数是要修改类的成员变量,所以类的构造函数不能申明成const类型的。
构造函数初始化式
与任何其他函数一样,构造函数具有名字、形参表和函数体。与其他函数不同的是,构造函数也可以包含一个构造函数初始化列表:
Sales_item::Sales_item(const string &book):
isbn(book),units_sold(0),revenue(0.0) { }
};
构造函数初始化列表以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个数据成员后面跟一个放在圆括号中的初始化式。这个函数将isbn成员初始化为book形参的值,将units_sold和revenue初始化为0。与任意的成员函数一样,构造函数可以定义在类的内部或外部。构造函数初始化是指在构造函数的定义中,而不是声明中指定。
构造函数初始化列表是许多相当有经验的C++程序员都没有掌握的一个特性。
构造函数初始化列表难以理解的一个原因在于,省略初始化列表并在构造函数的函数体内对数据成员赋值是合法的。可以将接受一个string的Sales_item构造函数编写为。
Sales_item::Sales_item(const string &book)
{
isbn=book;
units_sold=0;
revenue=0.0;
}
这个构造函数给类Sales_item的成员赋值,但没有进行显示初始化。不管是否有显示的初始化式,在执行构造函数前,要初始化isbn成员。这个构造函数隐式使用默认的string构造函数来初始化isbn。执行构造函数的函数体时,isbn成员已经有值了,该值被构造函数函数体中的赋值所覆盖。
从概念上讲,可以认为构造函数分为两个阶段执行:
- 初始化阶段。
- 普通的计算阶段,计算阶段由构造函数函数体中的所有语句组成。
注解:不管成员是否在构造函数初始化列表中显示初始化,类类型的数据成员总是在初始化阶段初始化,初始化发生在计算阶段开始之前。
重点(两种构造方法的区别):
再构造函数初始化列表中没有显示提及的每个成员,使用与初始化变量相同的规则来进行初始化。运行该类型的默认构造函数,来初始化类类型的数据成员。内置或复合类型的成员的初始值依赖于对象的作用域:在局部作用域中这些成员不被初始化,而在全局作用域中他们被初始化为0.
在样例编写的两个Sales_item构造函数版本具有同样的效果。无论是在构造函数初始化列表中初始化成员,还是在构造函数函数体中对它们赋值,最终结果是相同的。构造函数执行结束后,三个数据成员保存相同的值。不同之处在于,使用构造函数初始化列表的版本初始化数据成员,没有定义初始化列表的构造函数版本在构造函数函数体中对数据成员赋值。这个区别的重要性取决于数据成员的类型。