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); //这样就不行了,因为已经声明Sales_data的string构造函数是explicit的了
explicit Sales_data::Sales_data(istream& is){} //这样也不行,在外面了,explicit关键字只允许出现在类内的构造函数声明处
Sales_data item1(b); //这样可以
Sales_data item2 = item1; //不行,explicit声明的不能拷贝初始化

显式转换

item.combine(Sales_data(null_book));  
item.combine(static_cast<Sales_data>(cin));
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值