C++语法|函数返回一个类对象会发生什么?临时对象的深拷贝、返回值优化(ROV)和移动语义


C++ STL标准库解析|简单实现STL容器string代码和迭代器我们写了一个自己的string,然而在写operator+时,这里返回了一个对象,且其中的代码效率较低(具体可以回看文章 C++ STL标准库解析|简单实现STL容器string代码和迭代器的结语部分)。

那么我有一些疑问现在算是搞懂了,现在分享出来。

String operator+(const String &lhs, const String &rhs) {
	char *ptmp = new char[strlen(lhs._pstr) + strlen(rhs._pstr) + 1];
	strcpy(ptmp, lhs._pstr);
	strcat(ptmp, rhs._pstr);
	String tmp(ptmp);
	delete[] ptmp;
	return tmp;
}

在这段代码中我们一共经历了ptmp的内存开辟,字符串复制和连接操作和内存释放;tmp的构造和析构。其中涉及的操作太多,导致性能较低。

临时对象的深拷贝

首先我们需要弄清楚,返回一个类对象会发生什么呢?

在 return tmp 时,如果不考虑返回值优化(RVO),编译器会自动为我们做一个 tmp 的深拷贝,然后返回 tmp 的深拷贝。函数返回后,在该作用域析构 tmp 本身。

也就是说,在执行return tmp时,以下步骤会发生:

  • 编译器会调用tmp的拷贝构造函数,创建一个新的String对象,这个新对象是在调用函数的上下文中创建的。
  • 然后函数返回这个新创建的对象。
  • 原来的tmp对象在函数作用域结束时被析构。

返回值优化(ROV)

在现代 C++ 编译器中,返回值优化(RVO)是一种优化技术,可以在返回局部对象时避免不必要的深拷贝。在返回临时对象 tmp 时,编译器通常会直接构造返回值在调用者的上下文中,而不是在函数内部构造并复制到调用者上下文。这可以避免多余的复制和析构操作。

如果在没有 RVO 或者在一些情况下无法进行 RVO,C++11 引入了移动语义,可以通过移动构造函数来避免昂贵的深拷贝。当返回一个局部对象时,编译器可以选择使用移动构造函数,而不是复制构造函数。这意味着资源(如内存)可以从局部对象移动到新创建的对象,而不是复制它们。

移动语义

1. 移动构造函数

String(String&& other) noexcept : _pstr(other._pstr) {
    other._pstr = nullptr;
}

将资源从 other 移动到新对象,并将 other 的指针设置为 nullptr。

2.移动赋值运算符

String& operator=(String&& other) noexcept {
    if (this != &other) {
        delete[] _pstr;
        _pstr = other._pstr;
        other._pstr = nullptr;
    }
    return *this;
}

释放当前对象的资源,将资源从 other 移动到当前对象,并将 other 的指针设置为 nullptr。

通过这种方式,当函数返回局部对象 tmp 时,编译器可以使用移动构造函数来高效地返回对象,而不是进行深拷贝。这确保了返回值在函数结束后仍然有效,同时避免了不必要的性能开销

总结:

  • 如果编译器应用了 RVO,临时对象 tmp 将直接在返回值的位置上构造,不会调用拷贝或移动构造函数。
  • 如果编译器不能应用 RVO,它会调用移动构造函数来构造返回值,从而避免不必要的深拷贝。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值