我们先从一个简单的类开始
class Sales_item {
public:
Sales_item(const std::string &book)
: isbn(book),
units_sold(0),
revenue(0.0) {}
virtual ~Sales_item() {};
private:
std::string isbn;
unsigned units_sold;
double revenue;
};
1 构造函数
Sales_item(const std::string &book)
: isbn(book),
units_sold(0),
revenue(0.0) {}
构造函数是一个特殊的,与类同名的成员函数,并且不能指定返回类型,如上图所示。用于为每一个数据成员初始化。当创建一个对象的实例时,编译器会自动调用构造函数来初始化对象。
这里使用了初始化列表的形式来初始化对象的成员变量。初始化列表以":"开始,项与项之间用","分隔。
它可以没有形参,也可以定义多个形参。
除了初始化列表的形式,它还可以在函数体内对成员变量进行初始化,例如:
Sales_item::Sales_item(const string &book)
{
isbn = book;
units_sold = 0;
revenue = 0.0;
}
我们生成一个对象的实例时,可以采取下面两种方式:
Sales_item item("0-201-54848-8");
Sales_item *p = new Sales_item("0-201-54848-8");
2 默认构造函数
当一个类没有定义构造函数时,像这样:
class Sales_item {
};
这时,编译器会自动生成一个默认构造函数。
反过来,只要这个类定义了一个构造函数,那么编译器就不会再生成默认构造函数。
建议大家显式的定义一个默认构造函数。
使用默认构造函数生成一个对象的实例时应注意,下面的写法是错误的:
Sales_item item();
当没有形参时,这种写法不会引起编译错误,但是却未被编译器理解为一个函数,而不是一个类的对象。正确的写法应该是下面两种形式:
Sales_item item;
Sales_item item = Sales_item();
3 隐式类类型转换
在「C++语言基础(1):类型转换」一文中介绍了内置类型之间的几个自动转换。
也可以定义如何将其他类型的对象隐式转换为我们的类类型,或将我们的类类型对象隐式转换为其他类型。
为了定义到类类型的隐式转换,需要定义合适的构造函数。
3.1 单个形参构造函数的隐式类型转换
可以用单个实参来调用的构造函数定义了从形参类型到该类型的一个隐式转换。
class Sales_item {
public:
Sales_item(const std::string &book = "")
:isbn(book)
, units_sold(0)
, revenue(0.0)
{}
Sales_item(std::istream &is);
}
我们看上面的带有两个构造函数的Sales_item类。这里的每一个构造函数都定义了一个隐式转换。下述调用,都是合法的:
string null_book = "9-999-99999-9";
item.same_isbn(null_book);
item.same_isbn(std::cin);
编译器使用接受一个string或istream类型的Sales_item构造函数从null_book或cin生成一个新的Sales_item对象,并将该对象传递给same_isbn。但显然,这时的Sales_item对象是临时的,一旦same_item调用结束后,会被释放。这样的行为,多数情况下是错误的。
3.2 应抑制由构造函数定义的隐式转换
通常,除非有明显的理由想要定义隐式转换,否则,单形参构造函数应该为explicit。将构造函数设置为explicit可以避免错误,并且当转换有用时,用户可以显示的构造对象。
3.2.1 explicit
当构造函数被声明为explicit时,编译器将不使用它作为转换操作符。
class Sales_item {
public:
explicit Sales_item(const std::string &book = "")
:isbn(book)
, units_sold(0)
, revenue(0.0)
{}
explicit Sales_item(std::istream &is);
}
Sales_item::Sales_item(std::istream &is)
{
is >> *this;
}
如上所示,可以通过将构造函数声明为explicit,来防止在需要隐式转换的上下文中使用构造函数;该关键字只能用于类内部的构造函数声明上,在类的定义体外部所做的定义上不再重复它。
3.2.2 为转换而显式地使用构造函数
用explicit声明后的构造函数,如果需要做类型转换时,可以按照下面这样显式的调用:
string null_book = "9-999-99999-9";
item.same_isbn(Sales_item(null_book));