IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c++的RVO -> 正文阅读

[C++知识库]c++的RVO

返回值优化 RVO (return value optimization)
命名返回值优化 NRVO (named return value optimization)

这两个的关系应该是RVO包括NRVO(有命名对象的返回值优化 和无命名对象的返回值优化)
下面主要讨论的是NRVO

1.NRVO

#include <cstdio>
#include <iostream>

using namespace std;


class TestClass {
public:
    TestClass() { cout << "TestClass()" << endl; }
	TestClass(int v) { cout << "TestClass(int v)" << endl; }
    ~TestClass() { cout << "~TestClass()" << endl; }
    TestClass(const TestClass &obj) {
        this->v = obj.v;
        cout << "TestClass(const TestClass &obj)" << endl;
    }

    int v;
};

TestClass GetObj(int v) {
    TestClass t;
    if (v == 1) {
        return t;
    }
    return t;
}

TestClass GetObj2(int v) {
	TestClass t1;
    if (v == 1) {
        return t1;
    }
	TestClass t2;
    return t2;
}

int main() {
    cout << "support rvo:" << endl;
    TestClass t = GetObj(1);
	cout << endl;

    cout << "not support rvo:" << endl;
    TestClass t2 = GetObj2(1);
	cout << endl;
    return 0;
}

g++ test.cpp -o main
在这里插入图片描述
对于这个例子:
GetObj2不能进行返回值优化:
一次构造函数(TestClass t1;)
一次(外部赋值时)拷贝构造 (TestClass t2 = GetObj2(1);)
一次析构函数 (TestClass t2;;在函数结束后)

对于GetObj1能进行返回值优化,所以仅有一次构造,消除了拷贝构造函数

NRVO的原则:
函数返回的是同一个命名对象(GetObj2里返回了t1 和 t2 不是同一个,无法优化)

2.RVO的其他场景

为什么上面说RVO包括NRVO:
因为RVO还包括对于非命名对象的返回值优化:
下面这个例子:

#include <cstdio>
#include <iostream>

using namespace std;


class TestClass {
public:
    TestClass() { cout << "TestClass()" << endl; }
	TestClass(int v) { cout << "TestClass(int v)" << endl; }
    ~TestClass() { cout << "~TestClass()" << endl; }
    TestClass(const TestClass &obj) {
        this->v = obj.v;
        cout << "TestClass(const TestClass &obj)" << endl;
    }

    int v;
};

TestClass GetObj(int v) {
    TestClass t;
    if (v == 1) {
        return t;
    }
    return t;
}


TestClass GetObj3(int v) {
    if (v == 1) {
        return TestClass();
    }
    return TestClass(1);
}

int main() {
    cout << "support rvo:" << endl;
    TestClass t = GetObj(1);
	cout << endl;

	cout << "support rvo:" << endl;
	TestClass t3 = GetObj3(1);
    cout << endl;

    return 0;
}

在这里插入图片描述
返回的是没有命名的对象,直接调构造函数,哪怕调的构造函数不是同一个,都是可以进行返回值优化的

3.命名对象和非命名对象共存场景

#include <cstdio>
#include <iostream>

using namespace std;


class TestClass {
public:
    TestClass() { cout << "TestClass()" << endl; }
	TestClass(int v) { cout << "TestClass(int v)" << endl; }
    ~TestClass() { cout << "~TestClass()" << endl; }
    TestClass(const TestClass &obj) {
        this->v = obj.v;
        cout << "TestClass(const TestClass &obj)" << endl;
    }

    int v;
};

TestClass GetObj(int v) {
    TestClass t;
    if (v == 1) {
        return t;
    }
    return t;
}


TestClass GetObj4(int v) {
    TestClass t;
    if (v == 1) {
        return t;
    }
    return TestClass(1);
}

int main() {
    cout << "support rvo:" << endl;
    TestClass t = GetObj(1);
	cout << endl;

	cout << "not support rvo:" << endl;
	TestClass t4 = GetObj4(1);
    cout << endl;

    return 0;
}

此例子中 GetObj4既返回了命名对象t ,也返回了非命名对象TestClass(1);
这种场景是不能进行返回值优化的
在这里插入图片描述

4.总结

实际开发中,我们要避免GetObj2 和GetObj4的情况
即:
不要返回不同的命名对象、 不要命名对象、非命名对象混着在一个函数的不同分支中返回
就可以进行返回值优化的

p.s: 返回值优化是默认开启的,好像是在c++11之后的版本。有个编译选项可以关闭。有兴趣可以看看关闭后的表现。
(g++ test.cpp -o main -fno-elide-constructors)

5.有何意义?

理解返回值优化,有助于帮助我们写出更高效的代码。
比如说:optional 有时候我们使用optional,一旦用的不小心,就会出现不能进行返回值优化的情况

举个例子:在工作中检视其他同事的代码发现了这种写法

std::optional<Entity> Repo::Find(uint32_t ctpGemId) const
{
    auto opt = CtpGemEntityEncap().GetRecord(ctpGemId);
    if (!opt) {
        return {};
    }
 
    return std::make_optional<CtpGemEntity>(*opt);
}

修改为支持返回值优化的方式:


std::optional<Entity> Repo::Find(uint32_t idx) const
{
    std::optional<Entity> entity;
    std::optional<EntityRecord> opt = EntityEncap().GetRecord(idx);
    if (!opt) {
        return entity;
    }
 
    entity.emplace(*opt);
    return entity;
}

另外optional也是一个需要很小心使用的容器。后续再发一篇单独写一下optional的注意事项

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-05-08 07:51:17  更:2022-05-08 07:52:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 20:57:52-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码