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

这篇具有很好参考价值的文章主要介绍了20:宁以pass-by-reference-to-const替换pass-by-value。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

缺省条件下C++以by value方式传递对象至函数。除非你另外指定,否则函数参数都是以实际实参的复件(副本)为初值,而调用端所获得的亦是函数返回值的一个复件。这些复件(副本)有对象的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  plato;
bool platoIsok=validateStudent(plato);

当上述函数被调用时,会发生什么?

Student的copy构造函数会被调用,以plato为蓝本将s初始化,同样,当validateStudent返回s后,会被销毁。

但不仅仅是这样,Student对象内有两个string对象,所以每次构造一个Student对象也就构造了两个string对象,此外,Student对象继承自Person对象,所以每次构造Student对象也必须构造出一个Person对象。一个Person对象又有两个stirng对象在其中,因此每次Person构造动作又需承担两个string构造动作。

最终,以by value方式传递一个Student对象会导致调用一次Student copy构造函数、一次Person copy构造函数、四次string copy构造函数。当函数内的哪个Student复件被销毁,每一个构造函数调用动作需要一个对应的析构函数调用动作。

因此,以by value方式传递一个Student对象,总体成本是“六次构造函数和六次析构函数”。

尽管上述行为是正确的,但有没有什么方法可以回避那些构造和析构动作?

那就是pass by reference-to-const:

bool validateStudent(const Student& s);

这种传递方式的效率高得多:没有任何构造函数或析构函数被调用,因为没有任何新对象被创建。

注意,上述参数中含有const,因为原先以by value方式接受一个参数,因此调用者知道它们受到保护,函数绝不会对传入的Student作任何改变;valudateStudent只能对其副本做修改。现在Student以by reference方式传递,将它声明为const是必要的,因为不这样做的话,调用者不会知道validataStudent会不会改变他们传入的那个Student。

以by reference方式传递参数也可以避免slicing(对象切割)问题。当一个derived class以by value方式传递给本应接受base class的函数,它会被视作一个base class对象,base class的拷贝构造函数会被调用,而“造成此对象的行为像个derived class对象”的那些特化性质全被切割掉了,仅仅留下一个base class对象。

例:

class Window{
public:
    std::string name() const;//返回窗口名称
    virtual void display() const;//显示窗口和其内容
};
class WindowWithScrollBars: public Window{
public:
    virtual void display() const;
};

假设你希望写个函数打印窗口名称,然后显示该窗口。下面是错误的代码:

void printNameAndDsiplay(Window w)
{
    std::cout<<w.name();
    w.display();
}

 当你调用上述函数并交给它一个WindowWithScrollBars对象,会发生什么事?

WindowWithScrollBars wwsb;
printNameAndDisplay(wwsb);

wwsb会被构造成为一个Window对象,它的所有特化信息都会被切除,因此在printNameAndDisplay内调用display调用的总是Window::display,绝不会是WindowWithScrollBars::display。 

解决切割问题的方法是以by reference-to-const的方式传递w:

void printNameAndDisplay(const Window& w)//参数不会被切割
{
    std::cout<<w.name();
    w.display();
}

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

若你有个对象属于内置类型(例如int),pass by value往往比pass by reference的效率高。对于内置类型而言,当你有机会采用pass-by-value或pass-by-reference-to-const时,选择pass-by-value并非没有道理。这也适用于STL的迭代器和函数对象,因为习惯上它们都被设计为pass by value。

总结

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

2.以上规则并不适用于内置类型,以及STL的迭代器和函数对象。对它们而言,pass-by-value往往比较适当。 文章来源地址https://www.toymoban.com/news/detail-427753.html

到了这里,关于20:宁以pass-by-reference-to-const替换pass-by-value的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Ubuntu之apt-get--解决安装docker的报错:Package docker-ce is not available, but is referred to by another p

    原文网址:Ubuntu之apt-get--解决安装docker的报错:Package docker-ce is not available, but is referred to by another p_IT利刃出鞘的博客-CSDN博客 本文介绍用Ubuntu的apt-get命令安装docker时提示docker-ce不可用的解决方法。 Package docker-ce is not available, but is referred to by another package 此版本的源中没有d

    2024年02月02日
    浏览(62)
  • 侯捷 C++ part2 兼谈对象模型笔记——7 reference、const、new/delete

    7.1 reference x 是整数,占4字节; p 是指针占4字节(32位); r 代表 x ,那么 r 也是整数 ,占4字节 引用与指针不同,只能代表一个变量,不能改变 引用底部的实现也是指针,但是注意 object 和它的 reference 的 大小是相同的,地址也是相同的 (是编译器制造的假象) reference 通

    2024年02月12日
    浏览(32)
  • Relying upon circular references is discouraged and they are prohibited by default.循环依赖bug解决

    Relying upon circular references is discouraged and they are prohibited by default.循环依赖bug解决 出现的bug大概意思是:不鼓励依赖循环引用,默认情况下是禁止的。更新您的应用程序以删除 bean 之间的依赖循环。作为最后的手段,可以通过将 spring.main.allow-circular-references 设置为 true 来自动中

    2024年02月02日
    浏览(44)
  • CMake 链接时出现undefined reference to 错误

    一、问题背景 之前新建了一个项目项目文件分布为 1. src/MROR.cpp 2.include/MROR.h 3.main.cpp 执行cmake出现undefined reference toxx,显示main函数中的类成员函数调用没有声明,但是所有声明已经在MROR.h中写了 二、解决方法 我查了很多网上资料 解决方法 1) 可能MROR.cpp文件没有链接到项目,

    2024年02月09日
    浏览(59)
  • Mock实现单元测试报错:Argument passed to when( ) is not a mock!

    使用Mock进行单元测试时遇到了该问题:Argument passed to when( ) is not a mock! 首先翻译一下:传递给when( )方法的参数并不是一个Mock对象。 使用Mock时我们主要会用到@InjectMocks、@Mock和@Spy这三个注解,方法则主要是doReturn-when和when-thenReturn两种方式。 其中,@InjectMocks和@Spy创建的是一

    2024年02月12日
    浏览(35)
  • nmealib库编译提示 undefined reference to `ceil‘

    下载了nmealib库文件,默认工程进行编译,报错,提示如下: 网上搜索,因为使用了 ceil 数学相关的库,说是链接需要添加 -lm 的参数。 官方的 Makefile 文件 可以看到 LIBS 已经包含了 lm 。目前的报错该怎么解决呢? 解决办法:修改 LIBS 中 lm 的位置,放在后面。 如下: 再次编

    2024年01月20日
    浏览(38)
  • MySQL 8.0 Reference Manual(读书笔记20节-- NULL+模式匹配+外键+自增属性)

    The NULL value can be surprising until you get used to it. Conceptually【kənˈsɛptʃuəli 概念; 观念上; 概念上; 在概念上; 概念地; 】, NULL means “a missing unknown value” and it is treated somewhat differently from other values. To test for NULL, use the IS NULL and IS NOT NULL operators, as shown here:  You cannot use arithmetic comp

    2024年04月11日
    浏览(34)
  • 关于uniapp报警告Extraneous non-props attributes (info) were passed to component

    Extraneous non-props attributes (info) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. 在开发uniapp的过程中,遇到了这咩一个问题,如上面所述,也是感觉哪哪都对,就是报警告,像我这样强迫症,怎么会允许它有警告呢! 在网上找到一个

    2024年02月11日
    浏览(50)
  • Vitis HLS出现undefined reference to的可能解决办法

    问题描述: undefined reference to `cv::namedWindow(std::__cxx11::basic_stringchar, std::char_traitschar, std::allocatorchar const, int)\\\' undefined reference to `cv::resizeWindow(std::__cxx11::basic_stringchar, std::char_traitschar, std::allocatorchar const, int, int)\\\' undefined reference to `cv::imshow(std::__cxx11::basic_stringchar, std::char_traitscha

    2024年02月02日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包