zoukankan      html  css  js  c++  java
  • C++ Built-In Array 的语义

    C++ 编译花了大量精力使得class和原始类(primitive types)的用法一致。比如array的应用:

    A a[100];// A is class

    int b[100]

    虽然a是用户定义的类的对象,但是用起来与整数型的array相比并无差别。我们现在看看语义上的差别。

    A 是POD类(完全具有值语义的类)

    如果A是具有值语义的POD(参见我关于值语义的博客:http://www.cnblogs.com/ly8838/p/3929025.html ),测试显示A 的创建和读写与一般变量没有任何差别,当然性能也不会有差别。

    也就是说,A a[3] ;和 A a1, a2,a3 在语义上完全一样,也没有丝毫性能上的区别。

    A 是有默认的构造函数(但没有destructor)的类

    我们有一个简单的struct

    struct StackObject

    {

           int _a;

           int _b;

           StackObject(): _a(0), _b(1)

           {

           }

    };

    和简单的测试函数:

    void TestArraySemantics()

    {

           StackObject sa[10]; //line 1:call vector constructor iterator

           sa[0]._a= 1;

           sa[9]._b = 10;

                

    }

    在VC++2010中运行时,我们看到line1调用了编译自生的函数。这是一个通用的“矩阵构造循环(vector constructor iterator)”:

    它的大致实施如下:

    void Vector_constructor_iterator(

    int array_size,

    int array_element_size,

    void (*Ctr)(void *addr),

    char *arrayStartAddress)

    {

          for(int i = 0; i < array_size; ++i)

          {

                void *objAddr = arrayStartAddress + i * array_element_size;

                Ctr(objAddr);

          }

    }

    这是一个典型的 C 函数,它将 StackObject 的构造函数作为函数指针进行调用。

    从这个函数来看,它使得A a[3]A a1,a2, a3 的语义发生了根本变化。我们不但要调用编译产生的函数,还要用指针间接地调用StackObject的构造函数。测试结果显示,用array比不用array的“构造”速度大约下降30%。考虑到array的应用价值,这个速度的下降是可以理解和接受的。

    A 是有默认构造函数以及destructor的类

    加了destructor后,A a[10] 的语义又有了新的变化。如果读了我的上篇关于异常处理的博客(http://www.cnblogs.com/ly8838/p/3961119.html )可知:编译必须保证所有创建的object“全部”被摧毁,所以它必须“记住”创建过程中的热点。

    我们加另一个dummy 类,来测试destructor对array 的影响:

    struct StackObject2

    {

           int _a;

           int _b;

           StackObject2(): _a(0), _b(1)

           {

           }

           ~StackObject2()

           {

                 _a = _b = 0;

           }

    };

    我们的测试函数改为

    void TestArraySemantics()

           {

                 clock_t begin = clock();

                 for (int i = 0; i < 100000; ++i)

                 {           

                        StackObject sa[3]; //line1test without d’tr

                        sa[0]._a= 1;

                        sa[1]._b = 2;

                        sa[2]._b = 3;

                 }

                 clock_t end = clock();

                 auto spent = double(end - begin) / CLOCKS_PER_SEC;

                 printf("spent for 'array' is %f ", spent);

     

                 begin = clock();

                 for (int i = 0; i < 100000; ++i)

                 {           

                        StackObject2 sa[3]; //line2test with d’tr

                        sa[0]._a= 1;

                        sa[1]._b = 2;

                        sa[2]._b = 3;

                 }

                end = clock();

                 spent = double(end - begin) / CLOCKS_PER_SEC;

                 printf("spent for 'array with dtro' is %f ", spent);

     

                 begin = clock();

                 for (int i = 0; i < 100090; ++i)

                 {           

                        StackObject sa1, sa2, sa3; //line3test without array

                        sa1._a= 1;

                        sa2._b = 2;

                        sa3._b = 3;

                 }

               end = clock();

               spent = double(end - begin) / CLOCKS_PER_SEC;

                 printf("spent for 'none-array' is %f ", spent);

                

           }

     

    上面的line2试图创建带有destructorStackObject2类的array. 在VC++,我们注意到这时编译产生另一名字稍稍不同的函数“eh vector constructor iterator”,然而察看生成的代码,发现它和vector constructor iterator大大不同了。它的伪码大致如此: 

           void Vector_constructor_iterator_with_dtor(

                 int array_size,

                 int array_element_size,

                 void (*Ctr)(void *addr),

                 void (*Dtr)(void *addr),

                 char *arrayStartAddress)

           {

                 int lastCreated = -1;

                 try

                 {

                         for(int i = 0; i < array_size; ++i)

                         {

                               void *objAddr = arrayStartAddress + i * array_element_size;

                               Ctr(objAddr);

                               lastCreated = i;

                         }

                  }

                  catch(...)

                  {

                        // destroy partially created array in case or fault

                        for(int i = 0; i <= lastCreated; ++i)

                        {

                          void *objAddr = arrayStartAddress + i * array_element_size;

                          Dtr (objAddr);

                        }

                 }

           }

     比较伪码我们看出:Vector_constructor_iterator_with_dtor 和 Vector_constructor_iterator 的主要区别是增加了异常处理的机制,用来销毁“已经构造”的矩阵元素。

    运行 TestArraySemantics 表明,带有destructor的类的array构造速度下降了近 300%

    所以,去除不必要的destructor的重要性又一次充分体现。

    结论

    C++ built-in arrayclass object的支持是十分重要的语言构造,它是C++把class object和原始变量同样对待的又一反映,它大大增加了C++的附加值。

    然而我们一如既往,需要对C++这一语言构造的语义深入了解。以便正确使用C++ built-in array

  • 相关阅读:
    luogu 3388 【模板】割点(割顶)
    bzoj 3624 免费道路
    bzoj 1179 Atm
    bzoj 2428 均分数据
    luogu 4429 染色
    luogu 4427 求和
    luogu 1121 环状最大两段子段和
    hdu 4777 Queue
    hdu 5492 Find a path
    hdu 5441 Travel
  • 原文地址:https://www.cnblogs.com/ly8838/p/3963670.html
Copyright © 2011-2022 走看看