容器中应用三五法则和移动语义的效率探究
来源:互联网 发布:qq飞车暗夜幽灵数据 编辑:程序博客网 时间:2024/05/20 02:51
- 何谓三五法则
- 探究三五法则
- 首先给出一个基本类定义
- 类代码
- 测试 A在 vector 容器中应用三五法则和移动语义的效率分析
- 测试代码
- 结论
- 测试 B在 vector 容器中应用复杂自定义类型的三五法则和移动语义的效率分析
- 类代码
- 测试代码
- 结论
- 测试 C在函数中应用自定义类型的三五法则和移动语义的效率分析
- 函数定义代码
- 测试代码
- 结论
- 测试 D类中成员变量是引用类型
- 类代码
- 测试代码
- 结论
- 后记
- 首先给出一个基本类定义
何谓三/五法则
在《C++ Primer》中提出了三/五法则(P447 第五版)
直接引用前辈总结的内容吧:
原文
- 需要析构函数的类也需要拷贝构造函数和拷贝赋值函数
- 需要拷贝操作的类也需要赋值操作,反之亦然
- 析构函数不能是删除的
- 如果一个类有删除的或不可访问的析构函数,那么其默认和拷贝构造函数会被定义为删除的
- 如果一个类有const或引用成员,则不能使用合成的拷贝赋值操作
探究三五法则
首先给出一个基本类定义
该基本类包含:
- 空构造
- 拷贝构造
- 拷贝赋值
- 移动构造
- 移动赋值
- 析构函数
- 内置类型成员变量
类代码
class ExampleClass {public: // empty constructor ExampleClass() { m_x = 0; } // constructor ExampleClass(int x) : m_x(x) { cout << "ExampleClass constructor is called" << endl; } // copy constructor ExampleClass(const ExampleClass& example) { cout << "ExampleClass copy constructor is called" << endl; m_x = example.m_x; } // copy assign operator ExampleClass& operator= (const ExampleClass& example) { cout << "ExampleClass copy assign operator is called" << endl; if(this != &example) { this->m_x = example.m_x; } return *this; } // move constructor ExampleClass(ExampleClass&& example) { cout << "ExampleClass move constructor is called" << endl; m_x = example.m_x; } // move assign operator ExampleClass& operator= (ExampleClass&& example) { cout << "ExampleClass move assign operator is called" << endl; if(this != &example) { this->m_x = example.m_x; } return *this; } // destructor ~ExampleClass() { cout << "ExampleClass destructor is called" << endl; } int m_x;};
测试 A:在 vector 容器中应用三五法则和移动语义的效率分析
使用这个类,假如我有一个 vector 容器
测试代码
// functionA: 左值版本 push_back// void push_back (const value_type& val);// functionB: 右值版本 push_back// void push_back (value_type&& val);// functionC: 右值版本 emplace_back// template <class... Args>// void emplace_back (Args&&... args);void TestA() { vector<ExampleClass> exampleClassVector; cout << "use lvalue:" << endl; cout << "create lvalue example class object and push back" << endl; ExampleClass ec(1); exampleClassVector.push_back(ec); exampleClassVector.clear(); cout << "result: push back lvalue directly will use functionA" << endl; cout << "create lvalue example class object and push back by std::move" << endl; ExampleClass ec1(3); exampleClassVector.push_back(std::move(ec1)); exampleClassVector.clear(); cout << "result: if there has move constructor, then use functionB else use functionA" << endl; cout << "conclusion: lvalue should consider use std::move to push_back firstly" << endl; cout << "use rvalue:" << endl; cout << "create rvalue example class object and push back" << endl; exampleClassVector.push_back(ExampleClass(2)); exampleClassVector.clear(); cout << "result: push back rvalue directly will use functionB" << endl; cout << "create rvalue example class object and push back by std::move" << endl; exampleClassVector.push_back(std::move(ExampleClass(4))); exampleClassVector.clear(); cout << "result: use functionB, same as push back rvalue directly" << endl; cout << "create rvalue example class object by emplace back" << endl; exampleClassVector.emplace_back(5); exampleClassVector.clear(); cout << "result: use emplace_back to construct here" << endl; cout << "create rvalue example class object and emplace back" << endl; exampleClassVector.emplace_back(ExampleClass(6)); exampleClassVector.clear(); cout << "result: no need to do this" << endl; cout << "conclusion: rvalue should consider use emplace back firstly" << endl; cout << "end of test" << endl;}
结论
1、左值应该首先考虑用 std::move 添加至容器
2、右值应该首先考虑使用 emplace_back 添加至容器
测试 B:在 vector 容器中应用复杂自定义类型的三五法则和移动语义的效率分析
考虑更加复杂的情况:
含自定义类型的成员变量的类
类代码
class ExampleClassContainer {public: // empty constructor ExampleClassContainer() { cout << "ExampleClassContainer empty constructor is called" << endl; } // constructor: parameter use ExampleClass's copy constructor 2 times // ExampleClassContainer(ExampleClass exampleClass) : m_exampleClass(exampleClass) { // cout << "ExampleClassContainer constructor is called, with copy parameter" << endl; // } // constructor: parameter use ExampleClass's copy constructor 1 time ExampleClassContainer(ExampleClass exampleClass) { cout << "ExampleClassContainer constructor is called, with copy parameter" << endl; m_exampleClass = std::move(exampleClass); // use ExampleClass's move assign operator } // copy constructor ExampleClassContainer(const ExampleClassContainer& exampleClassContainer) : m_exampleClass(exampleClassContainer.m_exampleClass) { cout << "ExampleClassContainer copy constructor is called" << endl; } // copy assign operator ExampleClassContainer& operator= (const ExampleClassContainer& exampleClassContainer) { cout << "ExampleClassContainer copy assign operator is called" << endl; if(this != &exampleClassContainer) { this->m_exampleClass = exampleClassContainer.m_exampleClass; } return *this; } // move constructor ExampleClassContainer(ExampleClassContainer&& exampleClassContainer) /* : m_exampleClass(exampleClassContainer.m_exampleClass) use ExampleClass's copy constructor*/ { cout << "ExampleClassContainer move constructor is called" << endl; std::swap(m_exampleClass, exampleClassContainer.m_exampleClass); // use ExampleClass's move constructor and move assign operator 2 times } // move assign operator ExampleClassContainer& operator= (ExampleClassContainer&& exampleClassContainer) { cout << "ExampleClassContainer move assign operator is called" << endl; if(this != &exampleClassContainer) { // m_exampleClass = exampleClassContainer.m_exampleClass; // use ExampleClass's copy assign operator std::swap(m_exampleClass, exampleClassContainer.m_exampleClass); // use ExampleClass's move constructor and move assign operator 2 times } return *this; } ExampleClass m_exampleClass;};
测试代码
void TestB() { cout << "use lvalue:" << endl; cout << "create lvalue ExampleClass object" << endl; ExampleClass exampleClassA(1); cout << "create an ExampleClassContainer object by lvalue" << endl; ExampleClassContainer exampleClassContainerA(exampleClassA); cout << "create lvalue ExampleClassContainer object by std::move lvalue ExampleClass object" << endl; ExampleClassContainer exampleClassContainerB(std::move(exampleClassA)); cout << "create lvalue ExampleClassContainer object by rvalue ExampleClass object" << endl; ExampleClassContainer exampleClassContainerC(ExampleClass(2)); cout << "create lvalue ExampleClassContainer object by copy constructor" << endl; ExampleClassContainer exampleClassContainerD(exampleClassContainerC); cout << "assign lvalue ExampleClassContainer object by copy assign operator" << endl; exampleClassContainerD = exampleClassContainerB; cout << "create lvalue ExampleClassContainer object by move constructor" << endl; ExampleClassContainer exampleClassContainerE(std::move(exampleClassContainerA)); cout << "assign lvalue ExampleClassContainer object by move assign operator" << endl; exampleClassContainerE = std::move(exampleClassContainerD); cout << "conclusion: move or copy has nothing to do with combination" << endl; cout << "end of test" << endl;}
结论
移动或拷贝对复杂的组合类型没有影响
测试 C:在函数中应用自定义类型的三五法则和移动语义的效率分析
我们看看在传递参数的时候的情况
函数定义代码
/** * 值传递 * @param exampleClass 拷贝构造,右值无消耗 */void Func1(ExampleClass exampleClass) { cout << "Func1" << endl;}/** * 左值引用传递 * @param exampleClass 左值无消耗 */void Func2(const ExampleClass& exampleClass) { cout << "Func2" << endl;}/** * 右值引用传递 * @param exampleClass 右值无消耗 */void Func3(ExampleClass&& exampleClass) { cout << "Func3" << endl;}/** * 右值引用传递,值传递返回 * @param example 右值无消耗 * @return 值传递 */ExampleClass Func4(ExampleClass&& example) { cout << "Func4" << endl; return example;}/** * 右值引用传递,引用传递返回 * @param example 右值无消耗 * @return 引用传递 */ExampleClass& Func5(ExampleClass&& example) { cout << "Func5" << endl; return example;}/** * 值传递返回 * @return 左值 */ExampleClass Func6() { ExampleClass exampleClass(1); cout << "Func6" << endl; return exampleClass;}/** * 值传递返回 * @return 右值 */ExampleClass Func7() { cout << "Func7" << endl; return ExampleClass(1);}/** * 右值引用传递返回 * @return std::move 左值 */ExampleClass&& Func8() { ExampleClass exampleClass(1); cout << "Func8" << endl; return std::move(exampleClass);}/** * 右值引用传递返回 * @return 右值 */ExampleClass&& Func9() { cout << "Func9" << endl; return ExampleClass(1);}
测试代码
/** * 每个对象只应该调用一个 constructor 和 destructor * 多余的 constructor 和 destructor 都是额外消耗 */void TestC() { cout << "create lvalue ExampleClass object" << endl; ExampleClass exampleClassA(1); cout << "use lvalue as lvalue parameter" << endl; Func1(exampleClassA); // cost: copy constructor, destructor cout << "use std::move lvalue as lvalue parameter" << endl; Func1(std::move(exampleClassA)); // cost: move constructor, destructor cout << "use rvalue as lvalue parameter" << endl; Func1(ExampleClass(2)); // no cost cout << "use lvalue as lvalue reference" << endl; Func2(exampleClassA); // no cost cout << "use rvalue as lvalue reference" << endl; Func2(ExampleClass(3)); // no cost cout << "use lvalue as rvalue reference parameter by using std::move" << endl; Func3(std::move(exampleClassA)); // no cost cout << "use rvalue as rvalue reference parameter" << endl; Func3(ExampleClass(4)); // no cost cout << "use lvalue as rvalue reference parameter by using std::move and return value" << endl; ExampleClass exampleClassB = Func4(std::move(exampleClassA)); // cost: copy constructor cout << "use lvalue as rvalue reference parameter by using std::move and return reference" << endl; auto& exampleClassC = Func5(std::move(exampleClassA)); // no cost ExampleClass& exampleClassD = Func5(std::move(exampleClassA)); // no cost cout << "value return lvalue" << endl; ExampleClass exampleClassE = Func6(); // no cost cout << "value return rvalue" << endl; ExampleClass exampleClassF = Func7(); // no cost cout << "rvalue reference return std::move lvalue" << endl; ExampleClass&& exampleClassG = Func8(); // cost: destructor // cout << "rvalue reference return rvalue" << endl; // auto exampleClassH = Func9(); // 返回了一个临时变量的右值引用,bug cout << "end of test" << endl;}
结论
注意无消耗的那几条语句
测试 D:类中成员变量是引用类型
当成员变量是引用类型的时候
类代码
class ExampleClassContainerRef {public: // // empty constructor is invalid // ExampleClassContainerRef() { // // } // constructor should init mr_exampleClass: parameter use ExampleClass's copy constructor 1 time ExampleClassContainerRef(ExampleClass exampleClass) : mr_exampleClass(exampleClass) { cout << "ExampleClassContainerRef constructor is called" << endl; } // copy constructor should init mr_exampleClass: ExampleClassContainerRef(const ExampleClassContainerRef& exampleClassContainerRef) : mr_exampleClass(exampleClassContainerRef.mr_exampleClass) { cout << "ExampleClassContainerRef copy constructor is called" << endl; } // copy assign operator ExampleClassContainerRef& operator= (const ExampleClassContainerRef& exampleClassContainerRef) { cout << "ExampleClassContainerRef copy assign operator is called" << endl; if(this != &exampleClassContainerRef) { this->mr_exampleClass = exampleClassContainerRef.mr_exampleClass; } return *this; } // move constructor ExampleClassContainerRef(ExampleClassContainerRef&& exampleClassContainerRef) : mr_exampleClass(exampleClassContainerRef.mr_exampleClass) { cout << "ExampleClassContainerRef move constructor is called" << endl; } // move assign operator ExampleClassContainerRef& operator= (ExampleClassContainerRef&& exampleClassContainerRef) { cout << "ExampleClassContainerRef move assign operator is called" << endl; if(this != &exampleClassContainerRef) { // this->mr_exampleClass = exampleClassContainerRef.mr_exampleClass; std::swap(mr_exampleClass, exampleClassContainerRef.mr_exampleClass); // use ExampleClass's move constructor and move assign 2 times } return *this; } // destructor ~ExampleClassContainerRef() { cout << "ExampleClassContainerRef destructor is called" << endl; } ExampleClass& mr_exampleClass;};
测试代码
void TestD() { cout << "use lvalue:" << endl; cout << "create lvalue ExampleClass object" << endl; ExampleClass exampleClassA(1); cout << "exampleClassA address:" << &exampleClassA << endl; cout << "create an ExampleClassContainerRef object by lvalue" << endl; ExampleClassContainerRef exampleClassContainerRefA(exampleClassA); cout << "exampleClassContainerRefA.mr_exampleClass address:" << &exampleClassContainerRefA.mr_exampleClass << endl; cout << "create lvalue ExampleClassContainerRef object by std::move lvalue ExampleClass object" << endl; ExampleClassContainerRef exampleClassContainerRefB(std::move(exampleClassA)); cout << "exampleClassContainerRefB.mr_exampleClass address:" << &exampleClassContainerRefB.mr_exampleClass << endl; cout << "create lvalue ExampleClassContainerRef object by rvalue ExampleClass object" << endl; ExampleClassContainerRef exampleClassContainerRefC(ExampleClass(2)); cout << "exampleClassContainerRefC.mr_exampleClass address:" << &exampleClassContainerRefC.mr_exampleClass << endl; cout << "create lvalue ExampleClassContainerRef object by copy constructor" << endl; ExampleClassContainerRef exampleClassContainerRefD(exampleClassContainerRefC); // QUESTION: ExampleClass is no called? cout << "exampleClassContainerRefD.mr_exampleClass address:" << &exampleClassContainerRefD.mr_exampleClass << endl; // NOTE: no cost cout << "assign lvalue ExampleClassContainerRef object by copy assign operator" << endl; exampleClassContainerRefD = exampleClassContainerRefB; cout << "exampleClassContainerRefD.mr_exampleClass address:" << &exampleClassContainerRefD.mr_exampleClass << endl; cout << "create lvalue ExampleClassContainerRef object by move constructor" << endl; ExampleClassContainerRef exampleClassContainerRefE(std::move(exampleClassContainerRefA)); // QUESTION: ExampleClass is no called? cout << "exampleClassContainerRefE.mr_exampleClass address:" << &exampleClassContainerRefE.mr_exampleClass << endl; // NOTE: no cost cout << "assign lvalue ExampleClassContainerRef object by move assign operator" << endl; exampleClassContainerRefE = std::move(exampleClassContainerRefD); cout << "end of test" << endl;}
结论
注意无消耗的那几条语句
后记
以前一时兴起之作,后因年代久远,忘记探究初衷,将就着看吧,探究一下三五法则和拷贝移动语义在实际程序中发生了什么不是很有趣吗?尤其是那几个看上去是零消耗的操作。
CSDN 辣鸡 MD 编辑器,无序列表格式全丢
阅读全文
0 0
- 容器中应用三五法则和移动语义的效率探究
- 移动应用的成功法则
- boost中移动语义的支持
- 三五法则以及行为像值的类和行为像指针的类
- 三五法则以及行为像值的类和行为像指针的类
- DTP中语义组的应用
- C++三五法则
- C++之三五法则
- C++之三五法则
- 移动应用效率对比
- 探究ArrayList 和Map 的读取和插入效率
- for循环中i++与++i的效率探究
- for循环中i++与++i的效率探究
- for循环中i++与++i的效率探究
- for循环中i++与++i的效率探究
- for循环中i++与++i的效率探究
- for循环中i++与++i的效率探究
- for循环中i++与++i的效率探究
- python学习思维导图
- Android ViewPager+Fragment(碎片)实现页面滑动
- @JsonIgnore作用
- 移动端解决适配问题
- Datasets
- 容器中应用三五法则和移动语义的效率探究
- 不需要登录的app业务如何记录用户状态
- AJAX
- jquery.infinitescroll无限加载插件
- python监测mysql,并自动重启
- 使用pycharm学习python
- 高级控件-ViewPager
- SVG+JS path等值变化实现CSS3兴叹的图形动画
- Java学习之jdbc加强