博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
对象复制问题 && lvalue-rvalue && 引用
阅读量:7121 次
发布时间:2019-06-28

本文共 4700 字,大约阅读时间需要 15 分钟。

按值传递实参到函数和函数返回临时变量的副本,函数的效率对执行性能来说至关重要

如果避免这样的复制操作,则执行时间可能会大大缩短。

class CMessage{private:    char * m_pMessage;public:    void showIt()const    {        cout << m_pMessage << endl;    }    //构造函数    CMessage(const char* text = "Default message")    {        cout << " 构造函数" << endl;        size_t length{ strlen(text) + 1 };        m_pMessage = new char[length + 1];        strcpy_s(m_pMessage, length + 1, text);    }    //复制构造函数    CMessage(const CMessage & aMess)    {        cout << "复制构造函数" << endl;        size_t len{ strlen(aMess.m_pMessage) + 1 };        this->m_pMessage = new char[len];        strcpy_s(m_pMessage, len, aMess.m_pMessage);    }    //重载赋值运算符    CMessage & operator=(const CMessage & aMess)    {        cout << "重载赋值运算符函数" << endl;        if (this != &aMess)        {            delete[]m_pMessage;            size_t length{ strlen(aMess.m_pMessage) + 1 };            m_pMessage = new char[length];            strcpy_s(this->m_pMessage, length, aMess.m_pMessage);        }        return *this;    }    CMessage operator+(const CMessage & aMess)    {        cout <<"重载加法运算符函数" << endl;        size_t len{strlen(m_pMessage)+strlen(aMess.m_pMessage)+1};        CMessage message;        message.m_pMessage = new char[len];        strcpy_s(message.m_pMessage,len,m_pMessage);        strcat_s(message.m_pMessage,len,aMess.m_pMessage);        return message;    }    //析构函数    ~CMessage()    {        cout << " 析构函数" << endl;        delete[]m_pMessage;    }};int main(){    CMessage motto1{ "Amiss is " };    CMessage motto2{
"as good as a mile"}; CMessage motto3; motto3 = motto1 + motto2; motto3.showIt();}

运行结果如下:

 构造函数            //motto1调用

 构造函数            //motto2调用
 构造函数            //motto3调用
重载加法运算符函数        
 构造函数            //operator+()中message对象调用
复制构造函数           //返回时对message对象的复制,生成message的临时副本
 析构函数            //message调用,销毁临时对象
重载赋值运算符函数        //motto3调用operator=()
 析构函数              //message的临时副本调用
Amiss is as good as a mile
 析构函数
 析构函数
 析构函数

----------------------------------------------------------------------------------------------------------------------------------------------------------------

改进方法:应用rvalue引用形参

当源对象是一个临时对象,在复制操之后立即就被销毁时,复制的替代方案是偷用由 m_pMessage 成员指向的临时对象的内存,并传送到目标对象。

如果这么做,那么不需要为目标对象分配更多的内存,不需要复制对象,也不需要释放源对象拥有的内存。

在操作完成以后将立即销毁源对象,因此这么做没有风险,只是加快了执行速度。

实现此技术的关键是检测复制操作中何时是一个 rvalue。

  CMessage(const CMessage & aMess)

  {
      cout << "复制构造函数" << endl;
      size_t len{ strlen(aMess.m_pMessage) + 1 };
      this->m_pMessage = new char[len];
      strcpy_s(m_pMessage, len, aMess.m_pMessage);
   }

  CMessage(CMessage && aMess)

   {
      cout << "" << endl;
      m_pMessage = aMess.m_pMessage;
      aMess.m_pMessage = nullptr;      //必须要这么做,防止删除原指向的内存
    }

我们知道用对象初始化当前对象、返回临时对象都会调用复制构造函数。

motto3 = motto1 + motto2;调用重载加法运算符函数后会产生临时变量 message。

而对临时变量的复制产生需要的临时副本会增加运行时间。

所以,临时变量的副本可以直接“偷用”源临时变量对象成员指向的内存。通过以上两个函数比较可知,lvalue引用形参多了复制的操作。

难道要 lvalue引用形参的形式没有用了吗?

若: motto3=motto1 时,可知必须用 lvalue 引用形参的形式。如果 rvalue 可用,将会使两个对象同时指向一块内存。

所以, rvalue 针对 临时变量。

 

可以像下面这样额外创建 operator=()函数的重载

  CMessage & operator=(CMessage && aMess)

   {
      cout <<"Move assignment operator function called." << endl;
      delete[]m_pMessage;
      m_pMessage = aMess.m_pMessage;
      aMess.m_pMessage = nullptr;       //必须要这么做,防止删除原指向的内存
      return *this;
   }

  CMessage & operator=(const CMessage & aMess)

  {
     cout << "重载赋值运算符函数" << endl;
     if (this != &aMess)
     {   delete[]m_pMessage;
        size_t length{ strlen(aMess.m_pMessage) + 1 };
          m_pMessage = new char[length];
        strcpy_s(this->m_pMessage, length, aMess.m_pMessage);
     }
     return *this;
  }

观察这两个 lvalue、rvalue引用形参重载赋值运算符函数的区别:

motto3 = motto1 + motto2;调用重载加法运算符函数后会产生临时变量 message。在函数返回时又调用 rvalue引用形参类型的复制构造函数,

产生临时对象 message 的临时副本。

之后调用重载赋值运算符函数,由于此时仍是临时对象的副本,

所以,仍可以采用“ 偷换 ”源临时变量对象成员指向的内存。而避免赋值函数对对象成员的复制。

临时对象由编译器生成,使用之后会自动调用析构函数释放。

所以此处需要我们通过观察代码运行,自己来理解。

 

  CMessage operator+(const CMessage & aMess)

   {
    cout <<"重载加法运算符函数" << endl;
    size_t len{strlen(m_pMessage)+strlen(aMess.m_pMessage)+1};
    CMessage message;
    message.m_pMessage = new char[len];

    strcpy_s(message.m_pMessage,len,m_pMessage);

    strcat_s(message.m_pMessage,len,aMess.m_pMessage);

    return message;

   }

不知道你有没想过哟,为什么上面函数没有返回引用,引用可以避免不必要的复制,不是很方便吗?

添加引用 CMessage operator+(const CMessage & aMess)

运行结果:

   构造函数

   构造函数
   构造函数
  重载加法运算符函数
   构造函数
   析构函数
  重载赋值运算符函数
请按任意键继续. . .

发现程序崩溃,运行到重载赋值运算符函数就不能继续运行了。

Why?

如果被返回的对象是被调用函数中的局部变量,则不应按引用方式返回它。

因为,在被调用函数执行完毕时,局部对象将调用其 析构函数。

 

如果函数返回一个没有公有复制构造函数的类(如 ostream 类)的对象,它必须返回指向对象的引用。

如果在类中定义了 operator=()成员函数和复制构造函数时,将形参定义为非常量 rvalue 引用,则需要确保也定义了具有 const lvalue引用形参的标准版本。

编译器会提供它们的默认版本,逐一成员的进行复制。

转载于:https://www.cnblogs.com/yunqie/p/5947893.html

你可能感兴趣的文章
Windows下PowerShell监控Keepalived
查看>>
一个游戏程序员的学习资料
查看>>
UIMenuController,UIPasteboard:复制,粘贴详细解释
查看>>
js在以div添加滚动条
查看>>
thinkphp 如何调用百度echarts 数据报表插件
查看>>
Git异常:fatal: V1.0 cannot be resolved to branch.
查看>>
Atitit.web的自动化操作与信息抓取 attilax总结
查看>>
csdn android视频播放器开发
查看>>
lintcode:线段树的构造
查看>>
could not be installed at this time
查看>>
随机函数(Pascal入门)
查看>>
【NLP】蓦然回首:谈谈学习模型的评估系列文章(一)
查看>>
Java传值和传址
查看>>
两种常用的启动和关闭MySQL服务
查看>>
C# 事件
查看>>
一场改变你投资生涯的讨论:职业德州扑克手看交易
查看>>
IDEA 设置忽略那些文件不提交到SVN服务器
查看>>
HTTP的长连接和短连接
查看>>
ConcurrentHashMap并不是绝对线程安全的
查看>>
Oracle Instance
查看>>