类 - 2【C++ Primer 学习笔记 - 第十二章】

普通的非 const 成员函数中,this 是一个 const 指针。
指针所保存的地址不能改变,但是可以改变其指向的对象。

但是,const 成员函数中,this 就是一个 const 类型的 const 指针。

既不能改变 this 的指针值,也不能改变其指向的对象。


如果增加 const 成员函数 display ,在 cout 中打印内容。
则:display 返回的是 const 引用,因此,display 将无法嵌入到一个长表达式中。
代码如下:

myScreen.move(4, 0).set('#').display(cout);
myScreen.display(cout).set('#'); // 这里 display 返回的 const 引用,无法使用 set 方法。将报错

基于 const 的重载
为解决上述问题,必须定义两个 display 操作,一个是 const 的,一个是 非const 的。
const 对象只能使用 const 成员。非const 对象可以使用任一成员,非 const 成员是一个更好的匹配。


class Screen
{
public:	
	typedef string::size_type index;
	Screen& display(ostream &os)
	{
		do_display(os);
		return *this;
	}

	const Screen& display(ostream &os) const
	{
		do_display(os);
		return *this;
	}

private:
	void do_display(ostream &os) const
	{
		// 可变数据成员 access_ctr 即使在 const 成员函数中,
		// 也是可以修改的
		++access_ctr;
		os << contents;
	}

	// 可变数据成员 access_ctr 加上 mutable 关键字
	mutable size_t access_ctr;
	string contents;	
	index cursor;
	index height, width;
};
Screen myScreen(5,3);
const Screen blank(5,3);
// set 返回的是 非const 对象
// 因此,会匹配 display 的 非const 重载版本
myScreen.set('#').display(cout);

// blank 是 const 对象,匹配 display 的 const 重载版本
blank.display(cout);

构造函数不能声明为 const

class Sales_item
{
public:
	Sales_item() const;	// 报错
};


构造函数可以定义在 类的内部,或者外部
构造函数的初始化式只在构造函数的定义,而不是声明中指定

class Sales_item
{
public:
	Sales_item(const string&);
private:
	string isbn;
	int units_sold;
	double revenue;
};
// 显示初始化
Sales_item::Sales_item(const string &book):
isbn(book), units_sold(0), revenue(0.0){}

// 虽然,没有进行显示初始化,
// 但是,实际上,构造函数在执行之前,都要初始化 isbn 成员。
// 没有为类成员提供初始化,则编译器隐式地使用成员类型的默认构造函数
// 内置类型的成员,不会进行隐式初始化,
// 对于它们,进行初始化、或者在执行阶段进行赋值,都同样的效果。
// 在执行构造函数的函数体时,isbn 已经有值了,只是在执行之后,值被覆盖
// 构造函数分2个阶段执行:初始化阶段、函数体内普通的计算执行阶段
Sales_item::Sales_item(const string &book)
{
	isbn = book;
	units_sold = 0;
	revenue = 0.0;
}

某些成员必须在构造函数的初始化列表中进行初始化。
比如:没有默认构造函数的类类型成员、 const 或者引用类型 的成员。
非类类型的数据成员,进行赋值,或者,使用初始化式,在结果和性能上,是等价的。
但,有2个例外(const 类型、 引用类型)

class ConstRef
{
public:
	ConstRef(int ii);
private:
	int i;
	const int ci;
	int &ri;
};
ConstRef::ConstRef(int ii)
{
	i = ii;
	ci = ii;	// Error:const 类型,无法赋值
	ri = i;	// Error:引用类型,也要在初始化列表中指定
}
ConstRef::ConstRef(int ii):i(ii), ci(ii), ri(ii){};  // 正确

成员初始化的次序
成员被初始化的次序,是跟,成员定义的次序一致的。
通常,初始化的次序,无关紧要。但也有例外:

class X
{
	int i;
	int j;
public:
	// i先被定义,再定义 j
	// 因此,以下初始化的实际过程:
	// 先用未初始化的 j,来初始化i
	// 再用 val 初始化 j	
	X(int val):j(val), i(j){};
	
	// 建议写法如下:
	// 使用形参来初始化成员,初始化列表的顺序,
	// 跟成员定义的次序一致。
	// 并且避免使用对象的数据成员,来初始化其他成员
	// X(int val):i(val), j(val){};	
};

Sale_item(const string &book, int cnt, double price):
isbn(book), units_sold(cnt), revenue(cnt * price){}
// 初始化式,可以是任意的表达式(cnt * price)

类类型的数据成员的初始化式

Sale_item():isbn(10, '9'), units_sold(0), revenue(0) {}
// isbn(10, '9') 使用了 string 的构造函数,生成一个string,由10个9组成


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值