zoukankan      html  css  js  c++  java
  • C++ 性能剖析 (二):值语义 (value semantics)

     

    Value Semantics (值语义) 是C++的一个有趣的话题。

    什么是值语义? 简单的说,所有的原始变量(primitive variables)都具有value semantics. 也可以说,它们可以对应传统数学中的变量。有人也称它为POD (plain old data), 也就是旧时的老数据(有和 OOP 的新型抽象数据对比之意)。

    对一个具有值语义的原始变量变量赋值可以转换成内存的bit-wise-copy。

    对于用户定义的类呢?我们说,如果一个type X 具有值语义, 则:

    1)X 的size在编译时可以确定。

         这一点看似自然,其实在C++里有许多变量的size编译时无法确定。比如我在reference 三位一体里提到的polymorphic 变量,因为是“多身份”的,其(内容)的size是动态的。

    2)将X的变量x,赋值与另一个变量y,无须专门的 = operator,简单的bit-wise-copy 即可。

    3)当上述赋值发生后,x和y脱离关系:x 和 y 可以独立销毁, 其内存也可以独立释放。

    了解第三点很重要,比如下面的class A就不具备值语义:

    class A

    {

         char * p;

         public:

              A() { p = new char[10]; }

              ~A() { delete [] p; }

    };

    A 满足1和2,但不满足3。因为下面的程序会出错误: 

    Foo()

    {

        A a;

        A b = a;

    } // crash here

    改进的方法是定义一个A::operator=(constA &),并且用reference counting 的技术来保护指针,实现起来并不简单。所以我们说一旦一个class 失去了value semantics, 它也就失去了简单明了的 = 语义。

    从上面的分析可以得出结论,value semantics 有个简单的 = , 也正是数学意义上的 =

    学过Java, C#, 和JavaScript的程序员都知道,这些语言里的object都不具有值语义,因为它们都是指针,= 并不copy内容。也不满足条件3。

    那么value semantics 对C++性能有什么影响呢?我觉得有以下几方面:

    1)std 库是基于值语义 的。std container 包含的元素,都具有值语义. 不理解这一点,就不能正确使用std,也不会对std的性能,做出合理预期。

    2)简单的bit-wise-copy 赋值语句一般会提高赋值性能,因为它不需要特殊的 = operator 了。在使用std container 时,会有大量的copy 或assignment。 bit-wise-copy对于小的变量通常比函数划算得多。

    3)具有值语义的,size 不大的变量,在stack里,作为auto变量,传递,拷贝,释放全部和原始变量的用法完全一致,既好用,一般也具有优良的性能。动态语言缺乏这个(值语义)的语言构造和能力,(C# 有有限地支持:c# struct),所以速度上很难优化。

    4)注意,在设计具有值语义的类时,不要保留无用的destructor. destructor 的存在,使得你的类的语义和原始类有了本质的区别,C++ 编译会为此处心积虑地添加管理代码,使得一个简单的函数复杂化(会专门著文论证), 并且严重影响性能。这些当然是有附加值的,但是必须是设计需求的,而不是简单照搬的。

    5)不要保留无用的destructor这点,对使用 std 时更重要。我会专门论述。不要有臃余无用的 destructor 对任何类都是适用的,而对于有大量 copy, assignment 的 std container 尤为重要!

    那么,什么样的类没有值语义呢?我们不妨称这种型为 none-value-semantics type (NVST).

    1)有virtual function 的类

    2)包含NVST成员的类

    3)NVST 的衍生类(derived classed)

    4)定义了自己的 = operator 的类

    5)继承 virtual 基类的衍生类

    2014-6-21 西雅图

  • 相关阅读:
    Vue3源码系列之触发更新的实现
    Vue3源码系列之依赖收集的实现
    Vue3源码系列之reactiveApi实现
    删除链表的倒数第n个节点
    Shared_ptr 参考实现
    linux 目录结构 比较老
    C++11 bind function
    状态机DP
    尾递归
    秒杀系统的构建(2)
  • 原文地址:https://www.cnblogs.com/ly8838/p/3929025.html
Copyright © 2011-2022 走看看