• (转载)Comparing C++ and C (Inheritance and Virtual Functions)


    Virtual Functions and Inheritance

    This section presents the C++ code for a typical virtual function invocation scenario. This is then compared to the equivalent C code.

      1 // A typical example of inheritance and virtual function use.
    2
    3 // We would be mapping this code to equivalent C.
    4
    5 // Prototype graphics library function to draw a circle
    6 #include <iostream>
    7 #include <ctime>
    8 void glib_draw_circle (int x, int y, int radius);
    9
    10 // Shape base class declaration
    11
    12 class Shape
    13
    14 {
    15
    16 protected:
    17
    18 int m_x; // X coordinate
    19
    20 int m_y; // Y coordinate
    21
    22
    23
    24 public:
    25
    26 // Pure virtual function for drawing
    27
    28 virtual void Draw() = 0;
    29
    30
    31
    32 // A regular virtual function
    33
    34 virtual void MoveTo(int newX, int newY);
    35
    36
    37
    38 // Regular method, not overridable.
    39
    40 void Erase();
    41
    42
    43
    44 // Constructor for Shape
    45
    46 Shape(int x, int y);
    47
    48
    49
    50 // Virtual destructor for Shape
    51
    52 virtual ~Shape();
    53
    54 };
    55
    56
    57
    58 // Circle class declaration
    59
    60 class Circle : public Shape
    61
    62 {
    63
    64 private:
    65
    66 int m_radius; // Radius of the circle
    67
    68
    69
    70 public:
    71
    72 // Override to draw a circle
    73
    74 virtual void Draw();
    75
    76
    77
    78 // Constructor for Circle
    79
    80 Circle(int x, int y, int radius);
    81
    82
    83
    84 // Destructor for Circle
    85
    86 virtual ~Circle();
    87
    88 };
    89
    90
    91
    92 // Shape constructor implementation
    93
    94 Shape::Shape(int x, int y)
    95
    96 {
    97
    98 m_x = x;
    99
    100 m_y = y;
    101
    102 }
    103
    104
    105
    106 // Shape destructor implementation
    107
    108 Shape::~Shape()
    109
    110 {
    111
    112 //...
    113
    114 }
    115
    116 void Shape::MoveTo(int newX, int newY)
    117 {
    118 m_x=newX;
    119 m_y=newY;
    120 std::cout<<"MoveTo X:"<<m_x<<" Y:"<<m_y<<std::endl;
    121 }
    122
    123 void Shape::Erase()
    124 {
    125 m_x=0;
    126 m_y=0;
    127 std::cout<<"Erase"<<std::endl;
    128 }
    129 // Circle constructor implementation
    130
    131 Circle::Circle(int x, int y, int radius) : Shape (x, y)
    132
    133 {
    134
    135 m_radius = radius;
    136
    137 }
    138
    139
    140
    141 // Circle destructor implementation
    142
    143 Circle::~Circle()
    144
    145 {
    146
    147 //...
    148
    149 }
    150
    151
    152
    153 // Circle override of the pure virtual Draw method.
    154
    155 void Circle::Draw()
    156
    157 {
    158
    159 glib_draw_circle(m_x, m_y, m_radius);
    160
    161 }
    162
    163
    164
    165 void glib_draw_circle (int x, int y, int radius)
    166 {
    167 std::cout <<"draw circle"<<" x:"<<x<<" y:"<<y<<" radius:"<<radius<<std::endl;
    168 }
    169 int main()
    170
    171 {
    172 clock_t start,finish;
    173
    174 start = clock();
    175
    176 // Define a circle with a center at (50,100) and a radius of 25
    177
    178 Shape *pShape = new Circle(50, 100, 25);
    179
    180
    181
    182 // Define a circle with a center at (5,5) and a radius of 2
    183
    184 Circle aCircle(5,5, 2);
    185
    186
    187
    188 // Various operations on a Circle via a Shape pointer
    189
    190 pShape->Draw();
    191
    192 pShape->MoveTo(100, 100);
    193
    194 pShape->Erase();
    195
    196 delete pShape;
    197
    198
    199
    200 // Invoking the Draw method directly
    201
    202 aCircle.Draw();
    203 finish = clock();
    204
    205 std::cout <<"execute time:" <<( (double)(finish - start)*1000000/CLOCKS_PER_SEC )<<"us"<<std::endl;
    206 return 0;
    207
    208 }

    C code implementing the above C++ functionality is shown below. The code also includes compiler generated constructs like vtables. Virtual function access using vtables is also covered. (The presentation here has been simplified here to aid understanding).

    C Code
      1 /*
    2
    3 The following code maps the C++ code for the Shape and Circle classes
    4
    5 to C code.
    6
    7 */
    8 #include <stdio.h>
    9
    10 #include <stdlib.h>
    11 #include <time.h>
    12 #define TRUE 1
    13
    14 #define FALSE 0
    15
    16 typedef int BOOLEAN;
    17
    18 /*
    19
    20 Error handler used to stuff dummy VTable
    21
    22 entries. This is covered later.
    23
    24 */
    25
    26 void pure_virtual_called_error_handler();
    27 /* Prototype graphics library function to draw a circle */
    28 void glib_draw_circle (int x, int y, int radius);
    29 typedef void (*VirtualFunctionPointer)();
    30 /*
    31
    32 VTable structure used by the compiler to keep
    33
    34 track of the virtual functions associated with a class.
    35
    36 There is one instance of a VTable for every class
    37
    38 containing virtual functions. All instances of
    39
    40 a given class point to the same VTable.
    41
    42 */
    43
    44 typedef struct VTable
    45 { /*
    46 d and i fields are used when multiple inheritance and virtual
    47 base classes are involved. We will be ignoring them for this
    48 discussion.
    49
    50 */
    51
    52 int d;
    53 int i;
    54 /*
    55
    56 A function pointer to the virtual function to be called is stored here.
    57
    58 */
    59 VirtualFunctionPointer pFunc;
    60 }VTable;
    61
    62
    63
    64 /*
    65
    66 The Shape class maps into the Shape structure in C. All
    67
    68 the member variables present in the class are included
    69
    70 as structure elements. Since Shape contains a virtual
    71
    72 function, a pointer to the VTable has also been added.
    73
    74 */
    75
    76 typedef struct Shape
    77 {
    78
    79 int m_x;
    80 int m_y;
    81 /*
    82
    83 The C++ compiler inserts an extra pointer to a vtable which
    84
    85 will keep a function pointer to the virtual function that
    86
    87 should be called.
    88
    89 */
    90 VTable *pVTable;
    91 }Shape;
    92
    93
    94
    95 /*
    96
    97 Function prototypes that correspond to the C++ methods
    98
    99 for the Shape class,
    100
    101 */
    102
    103 Shape *Shape_Constructor(Shape *this_ptr, int x, int y);
    104
    105 void Shape_Destructor(Shape *this_ptr, BOOLEAN dynamic);
    106
    107 void Shape_MoveTo(Shape *this_ptr, int newX, int newY);
    108
    109 void Shape_Erase(Shape *this_ptr);
    110
    111
    112
    113 /*
    114
    115 The Shape vtable array contains entries for Draw and MoveTo
    116
    117 virtual functions. Notice that there is no entry for Erase,
    118
    119 as it is not virtual. Also, the first two fields for every
    120
    121 vtable entry are zero, these fields might have non zero
    122
    123 values with multiple inheritance, virtual base classes
    124
    125 A third entry has also been defined for the virtual destructor
    126
    127 */
    128 VTable VTableArrayForShape[] =
    129 { /*
    130
    131 Vtable entry virtual function Draw.
    132
    133 Since Draw is pure virtual, this entry
    134
    135 should never be invoked, so call error handler
    136
    137 */
    138
    139 { 0, 0, (VirtualFunctionPointer) pure_virtual_called_error_handler},
    140 /*
    141
    142 This vtable entry invokes the base class's
    143 MoveTo method.
    144 */
    145 { 0, 0, (VirtualFunctionPointer)Shape_MoveTo },
    146 /* Entry for the virtual destructor */
    147 { 0, 0, (VirtualFunctionPointer)Shape_Destructor }
    148
    149 };
    150
    151 /*
    152
    153 The struct Circle maps to the Circle class in the C++ code.
    154
    155 The layout of the structure is:
    156
    157 - Member variables inherited from the the base class Shape.
    158
    159 - Vtable pointer for the class.
    160
    161 - Member variables added by the inheriting class Circle.
    162
    163 */
    164
    165 typedef struct Circle
    166 {
    167 /* Fields inherited from Shape */
    168 int m_x;
    169 int m_y;
    170 VTable *pVTable;
    171 /* Fields added by Circle */
    172 int m_radius;
    173 }Circle;
    174
    175 /*
    176 Function prototypes for methods in the Circle class.
    177 */
    178 Circle *Circle_Constructor(Circle *this_ptr, int x, int y, int radius);
    179 void Circle_Draw(Circle *this_ptr);
    180 void Circle_Destructor(Circle *this_ptr, BOOLEAN dynamic);
    181 /* Vtable array for Circle */
    182 VTable VTableArrayForCircle[] =
    183 { /*
    184 Vtable entry virtual function Draw.
    185 Circle_Draw method will be invoked when Shape's
    186 Draw method is invoked
    187 */
    188 { 0, 0, (VirtualFunctionPointer) Circle_Draw },
    189 /*
    190
    191 This vtable entry invokes the base class's
    192
    193 MoveTo method.
    194
    195 */
    196 { 0, 0, (VirtualFunctionPointer) Shape_MoveTo },
    197
    198 /* Entry for the virtual destructor */
    199
    200 { 0, 0, (VirtualFunctionPointer) Circle_Destructor }
    201 };
    202
    203 Shape *Shape_Constructor(Shape *this_ptr, int x, int y)
    204 {
    205 /* Check if memory has been allocated for struct Shape. */
    206 if (this_ptr == NULL)
    207 {
    208 /* Allocate memory of size Shape. */
    209 this_ptr = (Shape *) malloc(sizeof(Shape));
    210 }
    211
    212 /*
    213 Once the memory has been allocated for Shape,
    214 initialise members of Shape.
    215 */
    216 if (this_ptr)
    217 { /* Initialize the VTable pointer to point to shape */
    218 this_ptr->pVTable = VTableArrayForShape;
    219 this_ptr->m_x = x;
    220 this_ptr->m_y = y;
    221 }
    222 return this_ptr;
    223 }
    224
    225
    226
    227 void Shape_Destructor(Shape *this_ptr, BOOLEAN dynamic)
    228 { /*
    229
    230 Restore the VTable to that for Shape. This is
    231
    232 required so that the destructor does not invoke
    233
    234 a virtual function defined by a inheriting class.
    235
    236 (The base class destructor is invoked after inheriting
    237
    238 class actions have been completed. Thus it is not
    239
    240 safe to invoke the ineriting class methods from the
    241
    242 base class destructor)
    243
    244 */
    245 this_ptr->pVTable = VTableArrayForShape;
    246
    247 /*
    248
    249 If the memory was dynamically allocated
    250
    251 for Shape, explicitly free it.
    252
    253 */
    254 if (dynamic)
    255 {
    256 free(this_ptr);
    257 }
    258 }
    259
    260 void Shape_MoveTo(Shape *this_ptr, int newX, int newY)
    261 {
    262 this_ptr->m_x = newX;
    263 this_ptr->m_y = newY;
    264 printf("MoveTo X:%d Y:%d \n",newX,newY);
    265 }
    266
    267 void Shape_Erase(Shape *this_ptr)
    268 {
    269 this_ptr->m_x = 0;
    270 this_ptr->m_y = 0;
    271 printf("Erase\n");
    272 }
    273
    274 Circle *Circle_Constructor(Circle *this_ptr, int x, int y, int radius)
    275
    276 {
    277
    278 /* Check if memory has been allocated for struct Circle. */
    279
    280 if (this_ptr == NULL)
    281
    282 {
    283 /* Allocate memory of size Circle. */
    284 this_ptr = (Circle *) malloc(sizeof(Circle));
    285 }
    286
    287
    288
    289 /*
    290
    291 Once the memory has been allocated for Circle,
    292
    293 initialise members of Circle.
    294
    295 */
    296
    297 if (this_ptr)
    298 { /* Invoking the base class constructor */
    299 Shape_Constructor((Shape *)this_ptr, x, y);
    300 this_ptr->pVTable = VTableArrayForCircle;
    301 this_ptr->m_radius = radius;
    302 }
    303 return this_ptr;
    304 }
    305
    306
    307
    308 void Circle_Destructor(Circle *this_ptr, BOOLEAN dynamic)
    309 {
    310 /* Restore the VTable to that for Circle */
    311 this_ptr->pVTable = VTableArrayForCircle;
    312 /*
    313
    314 Invoke the base class destructor after ineriting class
    315
    316 destructor actions have been completed. Also note that
    317
    318 that the dynamic flag is set to false so that the shape
    319
    320 destructor does not free any memory.
    321
    322 */
    323
    324 Shape_Destructor((Shape *) this_ptr, FALSE);
    325 /*
    326 If the memory was dynamically allocated
    327 for Circle, explicitly free it.
    328
    329 */
    330 if (dynamic)
    331 {
    332 free(this_ptr);
    333 }
    334 }
    335
    336
    337
    338 void Circle_Draw(Circle *this_ptr)
    339 {
    340 glib_draw_circle(this_ptr->m_x, this_ptr->m_y, this_ptr->m_radius);
    341 }
    342
    343 void pure_virtual_called_error_handler()
    344 {
    345 }
    346 void glib_draw_circle (int x, int y, int radius)
    347 {
    348 printf("draw circle x:%d y:%d radius:%d \n",x,y,radius);
    349 }
    350
    351 void main()
    352 {
    353 clock_t start,finish;
    354 /*
    355
    356 Dynamically allocate memory by passing NULL in this arguement.
    357
    358 Also initialse members of struct pointed to by pShape.
    359
    360 */
    361
    362 Shape *pShape = NULL;
    363
    364 /* Define a local variable aCircle of type struct Circle. */
    365
    366 Circle aCircle;
    367
    368 start=clock();
    369
    370 pShape = (Shape *) Circle_Constructor(NULL, 50, 100, 25);
    371 /* Initialise members of struct variable aCircle. */
    372
    373 Circle_Constructor(&aCircle, 5, 5, 2);
    374 /*
    375
    376 Virtual function Draw is called for the shape pointer. The compiler
    377
    378 has allocated 0 offset array entry to the Draw virtual function.
    379
    380 This code corresponds to "pShape->Draw();"
    381
    382 */
    383 (pShape->pVTable[0].pFunc)(pShape);
    384
    385 /*
    386
    387 Virtual function MoveTo is called for the shape pointer. The compiler
    388
    389 has allocared 1 offset array entry to the MoveTo virtual function.
    390
    391 This code corresponds to "pShape->MoveTo(100, 100);"
    392
    393 */
    394
    395 (pShape->pVTable[1].pFunc)(pShape, 100, 100);
    396
    397 /*
    398
    399 The following code represents the Erase method. This method is
    400
    401 not virtual and it is only defined in the base class. Thus
    402
    403 the Shape_Erase C function is called.
    404
    405 */
    406
    407 Shape_Erase(pShape);
    408
    409 /* Delete memory pointed to by pShape (explicit delete in original code).
    410
    411 Since the destructor is declared virtual, the compiler has allocated
    412
    413 2 offset entry to the virtual destructor
    414
    415 This code corresponds to "delete pShape;".
    416
    417 */
    418
    419 (pShape->pVTable[2].pFunc)(pShape, TRUE);
    420
    421 /*
    422
    423 The following code corresponds to aCircle.Draw().
    424
    425 Here the compiler can invoke the method directly instead of
    426
    427 going through the vtable, since the type of aCircle is fully
    428
    429 known. (This is very much compiler dependent. Dumb compilers will
    430
    431 still invoke the method through the vtable).
    432
    433 */
    434
    435 Circle_Draw(&aCircle);
    436
    437
    438 /*
    439
    440 Since memory was allocated from the stack for local struct
    441
    442 variable aCircle, it will be deallocated when aCircle goes out of scope.
    443
    444 The destructor will also be invoked. Notice that dynamic flag is set to
    445
    446 false so that the destructor does not try to free memory. Again, the
    447
    448 compiler does not need to go through the vtable to invoke the destructor.
    449
    450 */
    451
    452 Circle_Destructor(&aCircle, FALSE);
    453
    454 finish=clock();
    455 printf("execute time:%f us\n",(double)(finish - start)*1000000/CLOCKS_PER_SEC);
    456 }

    1 执行结果:

    C++代码

    draw circle x:50 y:100 radius:25
    MoveTo X:100 Y:100
    Erase
    draw circle x:5 y:5 radius:2
    execute time:22000us

    C代码

    draw circle x:50 y:100 radius:25
    MoveTo X:100 Y:100
    Erase
    draw circle x:5 y:5 radius:2
    execute time:7000.000000 us

    每次的执行时间会有差异,多次执行的结果C的时间是C++的1/3 ~1/2,因为具体的函数实现中很简单,主要是C++的构造、析构函数的时间比较多(?下面2的结果)。

    2 不执行具体的函数,只构造和销毁变量

    C++ Code
     1 void call2() //不执行函数,只构造和销毁变量
    2 {
    3 Shape *pShape = new Circle(50, 100, 25);
    4 Circle aCircle(5,5, 2);
    5 delete pShape;
    6 }
    7
    8 int main()
    9 {
    10 clock_t start,finish;
    11 int i=100000;
    12 start = clock();
    13 while(i--)
    14 {
    15 call2();
    16 }
    17 finish = clock();
    18
    19 std::cout <<"execute time:" <<( (double)(finish - start)*1000000/CLOCKS_PER_SEC )<<"us"<<std::endl;
    20 return 0;
    21
    22 }
    C Code
     1 void call2() //不执行函数,只构造和销毁变量
    2 {
    3 Shape *pShape = (Shape *) Circle_Constructor(NULL, 50, 100, 25);
    4 Circle aCircle;
    5 Circle_Constructor(&aCircle, 5, 5, 2);
    6 (pShape->pVTable[2].pFunc)(pShape, TRUE);
    7 Circle_Destructor(&aCircle, FALSE);
    8 }
    9 void main()
    10 {
    11 int i=100000;
    12 clock_t start,finish;
    13 start=clock();
    14 while(i--)
    15 {
    16 call2();
    17 }
    18 finish=clock();
    19 printf("execute time:%f us\n",(double)(finish - start)*1000000/CLOCKS_PER_SEC);
    20 }

    结果:

    D:\workspace\HEVC\C++vsC>Circle_C.exe
    execute time:38000.000000 us

    D:\workspace\HEVC\C++vsC>"Circle_C++.exe"
    execute time:39000us

    时间基本差不多

    3 函数执行为主体

    C++ Code
     1 void call3() //函数执行为主体
    2 {
    3 Shape *pShape = new Circle(50, 100, 25);
    4 Circle aCircle(5,5, 2);
    5 int i=1000000;
    6 while(i--);
    7 {
    8 pShape->Erase();
    9 }
    10 delete pShape;
    11 }
    C Code
    void call3() //函数执行为主体
    {
    int i=1000000;
    Shape *pShape = (Shape *) Circle_Constructor(NULL, 50, 100, 25);
    Circle aCircle;
    Circle_Constructor(&aCircle, 5, 5, 2);
    while(i--)
    {
    Shape_Erase(pShape);
    }
    (pShape->pVTable[2].pFunc)(pShape, TRUE);
    Circle_Destructor(&aCircle, FALSE);
    }

    结果:

    D:\workspace\HEVC\C++vsC>"Circle_C++.exe"
    execute time:3000us

    D:\workspace\HEVC\C++vsC>Circle_C.exe
    execute time:10000.000000 us

    C++比C快,C的时间是C++的3倍。

    4 开始的代码C比C++快的原因

    View Code
     1 C code
    2 void main()
    3 {
    4 clock_t start,finish;
    5 start=clock();
    6 printf("输出比较\n");
    7 finish=clock();
    8 printf("execute time:%f us\n",(double)(finish - start)*1000000/CLOCKS_PER_SEC);
    9 }
    10
    11 C++ code
    12 int main()
    13 {
    14 clock_t start,finish;
    15 start = clock();
    16 std::cout<<"输出比较"<<std::endl;
    17 finish = clock();
    18
    19 std::cout <<"execute time:" <<( (double)(finish - start)*1000000/CLOCKS_PER_SEC )<<"us"<<std::endl;
    20 return 0;
    21
    22 }

    D:\workspace\HEVC\C++vsC>"Circle_C++.exe"
    输出比较
    execute time:3000us

    D:\workspace\HEVC\C++vsC>Circle_C.exe
    输出比较
    execute time:1000.000000 us

    看来是C的输出函数比C++的效率要高。

    5 将1中main中的代码放在call函数中,去掉输出打印函数

    View Code
    C++ code 
    int main()
    {
    clock_t start,finish;
    int i=10000;
    start = clock();
    while(i--)
    {
    call();
    }
    finish = clock();

    std::cout <<"execute time:" <<( (double)(finish - start)*1000000/CLOCKS_PER_SEC )<<"us"<<std::endl;
    return 0;

    }
    C code
    void main()
    {
    clock_t start,finish;
    int i=10000;
    start = clock();
    while(i--)
    {
    call();
    }
    finish=clock();
    printf("execute time:%f us\n",(double)(finish - start)*1000000/CLOCKS_PER_SEC);
    }

    D:\workspace\HEVC\C++vsC>"Circle_C++.exe"
    execute time:4000us

    D:\workspace\HEVC\C++vsC>Circle_C.exe
    execute time:4000.000000 us

    时间一样。

    总结:C++转成C代码后,在类的构造、销毁与C的结构体的构造销毁上,效率没什么区别;而在具体函数的实现上,按照文章的转换方法,C的效率却只有C++的1/3,可以说类的成员函数调用方式比转换成C的直接调用函数的方式效率要高。

    但是C的输出函数printf比C++的cout效率要高,时间是C++的1 /3。




     

    原文地址 http://www.eventhelix.com/realtimemantra/basics/ComparingCPPAndCPerformance2.htm 

    源码下载地址 http://download.csdn.net/detail/mlj318/3748113 

  • 相关阅读:
    模拟退火、禁忌搜索、迭代局部搜索求解TSP问题Python代码分享
    多起点的局部搜索算法(multi-start local search)解决TSP问题(附Java代码及注释)
    爬取一定范围内的地图兴趣点并生成地点分布图
    Tabu Search求解作业车间调度问题(Job Shop Scheduling)-附Java代码
    Python爬虫系列
    干货 | 蚁群算法求解带时间窗的车辆路径规划问题详解(附Java代码)
    10分钟教你Python爬虫(下)--爬虫的基本模块与简单的实战
    vs code 打开文件时,取消文件目录的自动定位跟踪
    eclipse自动补全导致变量会跟上String后缀的问题解决
    16. nested exception is com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "auditUnitName"
  • 原文地址:https://www.cnblogs.com/mlj318/p/2233255.html
Copyright © 2020-2023  润新知