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。
|