C++箴言:用传引用给const取代传值

C++箴言:用传引用给const取代传值,第1张

C++箴言:用传引用给const取代传值,第2张

默认情况下,C++通过值将对象传入或传出函数(这是从C继承的一个特性)。除非您另外指定,否则函数的参数将使用实际参数的副本进行初始化,并且函数的调用方将收到函数返回值的副本。该副本由对象的复制构造函数生成。这使得按值传递成为一种开销很大的操作。例如,考虑以下类层次结构:

class Person {
public:
Person();//为简单起见省略参数
virtual ~ Person();//请参见第7项了解为什么这是虚拟的
...

private:
STD::string name;
std::string地址;
};

类学生:public Person {
public:
Student();//参数再次省略
~ Student();
...

private:
STD::string school name;
std::string学校地址;
};

现在,考虑下面的代码,其中我们调用一个函数-ValidateStudent,该函数获取一个学生参数(通过值的方式)并返回结果,无论它是否验证了有效性:

bool validateStudent(学生s);//通过值接受学生
//的函数

学生柏拉图;//柏拉图师从苏格拉底

bool platoIsOK = validateStudent(柏拉图);//调用函数

当这个函数被调用时会发生什么?

显然,调用Student的复制构造函数是为了用plato初始化参数s。同样,显而易见,当validateStudent返回时,S将被销毁。所以这个函数参数传递的代价就是调用Student的复制构造函数和调用Student的析构函数。

但这还不是全部。一个Student对象包含两个string对象,因此每次构造Student对象时,也必须构造两个string对象。Student对象也继承自Person对象,因此每次构造Student对象时,也必须构造Person对象。一个Person对象包含两个额外的string对象,所以每个Person的构造也承载了另外两个string的构造。最后,通过值传递一个学生对象的结果是导致一次调用Student的复制构造函数,一次调用Person的复制构造函数和四次调用string的复制构造函数。当Student对象的副本被销毁时,每个构造函数调用对应一个析构函数调用,所以通过值传递一个Student的总开销是6个构造函数和6个析构函数!

好吧,这是正确而值得的行为。毕竟,您希望所有对象都被可靠地初始化和销毁。尽管如此,如果有一种方法可以绕过所有这些构造和销毁过程,那应该会更好,那就是:通过引用传递到常数(pass by reference-to-const):

bool validate Student(const Student & s);

这非常有效:没有调用构造函数或析构函数,因为没有构造新的对象。Const在修改后的参数声明中非常重要。validateStudent的原始版本接受一个Student值参数,因此调用方知道他们对传入的Student屏蔽了函数的任何可能的更改;ValidateStudent只能更改它的一个副本。现在学生是通过引用传递的,需要声明为const。否则,调用方肯定担心validateStudent更改了他们传入的学生。

通过引用传递参数也可以避免切片问题。当一个派生类对象作为基类对象(通过值的方式)被传递时,基类的复制构造函数被调用,使对象行为像派生类对象的特殊特性被“砍掉”。你只剩下一个纯基类对象——这并不奇怪,因为它是创建它的基类的构造函数。这不是你想要的。例如,假设您处理一组实现图形窗口系统的类:

类窗口{
public:
...
STD::string name()const;//返回窗口名称
virtual void display()const;//绘制窗口和内容
};

类WindowWithScrollBars:公共窗口{
公共:
...
虚拟void display()const;
};

所有窗口对象都有一个名称。可以通过name函数得到,所有窗口都可以显示。您可以通过调用display函数来实现这一点。DisplayVirtual这个事实清楚地告诉你,一个纯基类的Window对象的显示方法可能与一个专门化的WindowWithScrollBars对象的显示方法不同。

位律师回复
DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
白度搜_经验知识百科全书 » C++箴言:用传引用给const取代传值

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情