600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > C++ 请以pass-by-reference-to-const替换pass-by-value

C++ 请以pass-by-reference-to-const替换pass-by-value

时间:2018-10-13 11:27:21

相关推荐

C++ 请以pass-by-reference-to-const替换pass-by-value

0312 C++ 请以pass-by-reference-to-const替换pass-by-value

缺省情况下C++以by value方式(一个继承自C的方式)传递对象至(或来自)函数,除非你另外指定,否则函数参数就是以实际实参的复件(副本)为初值,而调用端所获得的亦是函数返回值的一个复件。这些复件(副本)是由对象的copy构造函数产出,这可能使得pass-by-value成为昂贵的(费时的)操作。考虑以下class继承体系:

class Person{

public:

Person();//为求简化,省略参数

virtual ~Person();

...

private:

std::string name;

std::string address;

};

class Student: public Person{

public:

Student();

~Student();

...

private:

std::string schoolName;

std:;string schoolAddress;

};

现在考虑以下代码,其中调用函数validateStudent,后者需要一个Student实参(by value)并返回它是否有效:

bool validateStudent(Student s);//函数以by value方式接受学生

Student yanhui;//颜回,孔子的学生

bool yanhuiisOK = validateStudent(yanhui);//调用函数

上述函数被调用时,无疑的,Student的copy构造函数会被调用,以yanhui为蓝本将颜回初始化。同样明显地,当validateStudent返回s会被销毁。因此,对于函数而言,参数的传递成本是“一次Student 拷贝构造函数调用,加上一次Student析构函数被调用”。

详细过程为:Student对象内有两个string对象,所以每次构造一个Student对象也就构造了两个string对象,此Student对象继承自Person对象,所以每次构造Student对象也必须构造出一个Person对象。一个Person对象又有两个string对象在其中,因此每一次Person构造动作又需承担两个string构造动作。最终结果是,以by value方式传递一个Student对象会导致调用一次Student 拷贝构造函数,一次Person拷贝构造函数,四次string拷贝构造函数。当函数内的那个Student复件被销毁,每一个构造函数调用动作都需要一个对应的析构函数调用动作。因此,以by value方式传递一个Student对象,总体成本是“六次构造函数和六次析构函数”。

如果想出一个方法可以回避所有构造和析构函数就好了。有的!就是pass by reference-to-const:

bool validateStudent(const Student& s);

现在Student以by reference方式传递,将它声明为const是必要的,因为不这样做的话调用者会忧虑validateStudent会不会改变他们传入的那个Student。

以by reference方式传递参数也可以避免对象切割(slicing)问题。假设在一组classes上工作,用来实现一个图形窗口系统:

class Window{

public:

...

std::string name() const;//返回窗口名称

virtual void display() const;//显示窗口和其他内容

};

class WindowWithScrollBars:public Window{

public:

...

virtual void display() const;

};

所有Window对象都带有一个名称,你可以通过name函数取得它。所有窗口都可显示,你可以通过display函数完成它。display是一个virtual函数,这意味着简易朴素的基类Window对象的显示方式和华丽WindowWithScrollBars对象的显示方式不同。(派生类是基类的扩展)。

现在假设写个函数打印窗口名称,然后显示窗口,下面的示范是错误的:

void printNameAndDisplay(Window w)//不正确! 参数可能被切割

{

std::cout<<w.name();

w.display();

}

当调用上述函数并交给他WindowWithScrollingBars对象 会怎样呢?

WindowWithScrollingBars wwsb;

printNameAndDiaplay(wwsb);

参数w会被构造成一个Window对象;它是pass by value,而造成wwsb“是个WindowWithScrollingBars对象”的所有特化信息都会被切除。在printNameAndDisplay函数内不论传递过来的对象原本是什么类型,参数w就像一个Window对象(因为其类型的Window)。因此在printNameAndDisplay内调用display掉用的总是Window::display,绝对不会是WindowWithScrollingBars::diaplay。

解决切割(slicing)问题的办法,就是以by reference-to-const的方式传递w:

void printNameAndDisplay(const Window& w)//很好,参数不会被切割

{

std::cout<<w.name;

w.dispaly();

}

现在,传进来的窗口是什么类型,w就表现出那种类型。

在C++底层实现中,reference往往以指针实现,因此pass by reference通常意味着真正传递的是指针,因此若对象属于内置类型,pass by value往往比pass by reference效率更高,这种情况也适用于STL的迭代器和函数对象。

注意:

1、尽量以pass-by-reference-to-const替换pass-by-value。前者通常比较高效,并可以避免切割问题(slicing problem)。

2、以上规则并不适用于内置类型,以及STL的迭代器和函数对象。对它们而言,pass-by-value往往比较合适。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。