Effective C++|Chapter 4:设计与声明

Item 20:宁以pass-by-reference-to-const替代pass-by-value

  • 以下代码使用pass-by-value方法,调用validateStudent(Student s)的成本是“6次构造函数和6次析构函数”:1次Student copy构造函数调用,1次Student析构函数调用;由于Student继承Person,因此构造Student前需构造Person。1个Student对象有2个string,1个Person对象也有2个string,所以这里有对string对象的4次构造和4次析构调用。总共6次构造函数和6次析构函数的调用。

    class Person {
    public:
    	Person();
    	virtual ~Pearson();
    	...
    private:
    	string name;
    	string address;
    };
    class Student: public Person {
    public:
    	Student();
    	virtual ~Student();
    	...
    private:
    	string schoolName;
    	string schoolAddress;
    };
    
    bool validateStudent(Student s);
    Student plato;
    bool platoIsOK = validateStudent(plato);
    
  • 正确做法:pass by reference-to-const:【这还可以避免对象切割】

    bool validateStudent(const Student& s);
    
  • 对象切割示例:由于此处采用pass-by-value,printNameAndDisplay()无论传递的是什么类型的参数,函数接收的始终是Window类型,因此其内部的w.display();总是Window类型的调用。

    class Window {
    public:
    	...
    	string name() const;
    	virtual void display() const;
    };
    class WindowWithScrollBars: public Window {
    public:
    	...
    	virtual void display() const;
    };
    
    void printNameAndDisplay(Window w)
    {
    	cout << w.name() << endl;
    	w.display();
    }
    WindowWithScrollBars wwsb;
    printNameAndDisplay(wwsb);
    
  • 以上规则并不适用于内置类型,以及STL的迭代器和函数对象。对它们而言,pass-by-value往往比较适当。

Item23:宁以non-member、non-friend替换member函数【对封装的解释讲得很好】

  • member函数以及及friend函数可以访问private成员变量。而方法论是:越少人看到成员变量,意味着我们有越大的弹性去改变它,因为我们的改变仅仅影响到改变的那些人事物。这就是我们推崇封装的原因:它使我们能够改变事物而只影响有限客户。
  • 显然,non-member、non-friend会有较大的封装性性,因为它不增加”能够访问class内之private成分“的函数数量。

Item24:若所有参数皆需类型转换,请为此采用non-member函数

class Rational {
public:
	// 构造函数可以不为explicit,允许int-to-Rational隐式转换
	Rational(int numerator = 0, int demoniator = 1);
	// 分子和分母的访问函数
	int numerator() const;
	int demoniator() const;
	// 最前面的const说明只能是右值
	const Rational operator* (const Rational& rhs) const;
private:
	...
}

这个设计使你能够将两个有理数以最轻松自在的方式相乘:

Rational oneEighth(1, 8);
Rational oneHalf(1, 2);
Rational result = oneHalf * oneEighth; // 很好
result = result * oneHalf;  // 很好

// 这里将2隐式转换成Rational&
result =  oneHalf * 2; // 很好
// 由于2不是Rational class,因此它没有operator*成员函数;即只有当参数位于参数列内,这个参数才是饮食类型转换的合格参与者。
result =  2 * oneHalf;  // 错误!

result =  oneHalf.operator*(2);  // 很好
// 同理,由于2不是Rational class,因此它没有operator*成员函数
result = 2.operator*(onehalf);  // 错误!

// 由于2不是Rational class,因此它没有operator*成员函数;即只有当参数位于参数列内,这个参数才是饮食类型转换的合格参与者。

  • 若一定想要支持混合式算术运算,让operator*成为non-member函数,便允许编译器在每一个实参身上执行隐式类型转换:
class Rational {
	...
};
const Rational operator*(const Rational& lhs, const Rational& rhs) {
	return Rational(lhs.numerator() * rhs.numerator(), lhs.demoniator() * rhs.demoniator());
Rational oneFourth(1, 4);
Rational result;
Rational = oneFourth * 2// 没问题
result = 2 * oneFourth;  // 没问题
}

此处的operator*是非member函数,所以它的参数列位于左右两侧,所以编译器在每一个实参身上执行隐式类型转换。

  • 请记住:
    • 如果你需要为某个函数的所有参数(包括被this指针所指的那个隐参)进行类型转换,那么这个函数必须是个non-member。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值