C++ primer 7.5 构造函数再探

 

初始值列表

Sales_data::Sales_data(const string &s) : bookNo(s)  
{  

}  

Sales_data::Sales_data(const string &s)  
{  
    bookNo = s;  
}

这两个函数从执行的结果上看没有区别,但是

第一个函数执行的过程相当于

string foo = "abc";

定义并初始化

第二个函数执行的过程相当于

string foo;  
foo = "abc";

先定义、再赋值,且在定义时会执行默认初始化

第一个过程明显优于第二个

如果没有在构造函数的初始值列表中显式地初始化成员,则该成员将在构造函数体之前执行默认初始化

因此推荐使用初始值列表

初始值有时必不可少

在声明变量明:

  • 对于const或者引用类型的变量,必须初始化。
  • 对于没有定义默认构造函数的类,其对象必须初始化

类似的,当这些类型的变量是类的成员时,必须在初始值列表中初始化

初始化的顺序

并不是写在初始值列表前面的成员先被初始化

成员的初始化顺序与它们在类定义中出现的顺序一致

为了减少不必要的麻烦,尽量避免依赖于顺序的初始值列表

class X  
{  
private:  
    int i;  
    int j;  
public:  
    X(int val) : j(val), i(j){}  
};

尽量避免依赖于顺序,修改为

X(int val) : j(val), i(val){}

默认实参与构造函数

class Sales_data  
{  
public:  
    Sales_data(std::string s = "") : bookNo(s){}  
};

如果一个构造函数为所有参数都提供了默认实参,则它实际上也定义了默认构造函数

委托构造函数

c++11提供

一个委托构造使用它所属类的其他构造函数执行自己的初始化过程

class Sales_data
{
public:
    //这是函数一,是一个普通的构造函数
    Sales_data(string s, unsigned cnt, double price) : bookNo(s), units_sold(cnt), revenue(cnt*price){}

    //接下来就是各种偷懒方法了,注意看
    Sales_data(): Sales_data("", 0, 0){} //函数二是默认构造函数,委托函数一帮忙初始化,也可以认为是调用了函数一
    Sales_data(string s): Sales_data(s, 0, 0){} //函数三接受一个string参数,委托函数一帮忙初始化
    Sales_data(istream &is): Sales_data()
    {
        read(is, *this);
    }
    //函数四复杂些,它先委托函数二,就是默认构造函数,函数二去委托函数一,这些函数执行完成后,再执行函数四的函数体
    //调用read函数读取给定的istream
};

后面的几个构造函数都委托第一个构造函数执行自己的初始化过程

委托构造函数的执行过程:

  • 被委托者的初始化列表
  • 被委托者的函数体
  • 委托者的函数体

默认构造函数的作用

当对象被默认初始化或值初始化时自动执行默认构造函数。

在实际中,如果定义了其他构造函数,最好也提供一个默认构造函数。

Sales_data obj;

执行默认构造函数的逻辑

隐式的类类型转换

如果构造函数只接受一个参数,则它实际上定义了转换为此类类型的隐式转换机制。我们把这种构造函数称作转换构造函数。

string null_book = "999";  
item.combine(null_book);

上面的代码是合法的,但是我们知道combine的参数实际应当是Sales_data类型

Sales_data &combine(const Sales_data&);

所以这里存在一个隐式的类类型转换过程:

  • 编译器用给定的string自动创建了一个Sales_data对象
  • 新生成的这个临时对象被传递给combine

只允许一步类类型转换

编译器只会自动地执行一步类型转换

下面的代码隐式地使用了两种转换规则,所以它是错误的

item.combine("999");

正确的写法是

item.combine(string("999");  
item.combine(Sales_data("999"));

抑制构造函数定义的隐式转换

通过将构造函数声明为explicit来阻止

  • 关键字explicit只对一个实参的构造函数有效
  • 在类外部定义成员函数时不需要使用explicit关键字
class Sales_data
{
public:
    Sales_data() = default;
    explicit Sales_data(const string &s): bookNo(s){}
    explicit Sales_data(istream&);
};

Sales_data item;
string b = "1"
item.combine(b); //这样就不行了
explicit Sales_data::Sales_data(istream& is){} //这样也不行,在外面了
Sales_data item1(b); //这样可以
Sales_data item2 = item1; //不行,explicit声明的不能拷贝初始化

显式转换

item.combine(Sales_data(null_book));  
item.combine(static_cast<Sales_data>(cin));

猜你喜欢

转载自blog.csdn.net/zpznba/article/details/84839973
今日推荐