引述自:http://www.eventhelix.com/realtimemantra/basics/ComparingCPPAndCPerformance2.htm#.UaG1Kdd3-fg
1 // A typical example of inheritance and virtual function use.
2 // We would be mapping this code to equivalent C.
3
4 // Prototype graphics library function to draw a circle
5 void glib_draw_circle (int x, int y, int radius);
6
7 // Shape base class declaration
8 class Shape
9 {
10 protected:
11 int m_x; // X coordinate
12 int m_y; // Y coordinate
13
14 public:
15 // Pure virtual function for drawing
16 virtual void Draw() = 0;
17
18 // A regular virtual function
19 virtual void MoveTo(int newX, int newY);
20
21 // Regular method, not overridable.
22 void Erase();
23
24 // Constructor for Shape
25 Shape(int x, int y);
26
27 // Virtual destructor for Shape
28 virtual ~Shape();
29 };
30
31 // Circle class declaration
32 class Circle : public Shape
33 {
34 private:
35 int m_radius; // Radius of the circle
36
37 public:
38 // Override to draw a circle
39 virtual void Draw();
40
41 // Constructor for Circle
42 Circle(int x, int y, int radius);
43
44 // Destructor for Circle
45 virtual ~Circle();
46 };
47
48 // Shape constructor implementation
49 Shape::Shape(int x, int y)
50 {
51 m_x = x;
52 m_y = y;
53 }
54
55 // Shape destructor implementation
56 Shape::~Shape()
57 {
58 //...
59 }
60
61 // Circle constructor implementation
62 Circle::Circle(int x, int y, int radius) : Shape (x, y)
63 {
64 m_radius = radius;
65 }
66
67 // Circle destructor implementation
68 Circle::~Circle()
69 {
70 //...
71 }
72
73 // Circle override of the pure virtual Draw method.
74 void Circle::Draw()
75 {
76 glib_draw_circle(m_x, m_y, m_radius);
77 }
78
79 main()
80 {
81 // Define a circle with a center at (50,100) and a radius of 25
82 Shape *pShape = new Circle(50, 100, 25);
83
84 // Define a circle with a center at (5,5) and a radius of 2
85 Circle aCircle(5,5, 2);
86
87 // Various operations on a Circle via a Shape pointer
88 pShape->Draw();
89 pShape->MoveTo(100, 100);
90 pShape->Erase();
91 delete pShape;
92
93 // Invoking the Draw method directly
94 aCircle.Draw();
95 }
1 /*
2 The following code maps the C++ code for the Shape and Circle classes
3 to C code.
4 */
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #define TRUE 1
9 #define FALSE 0
10 typedef int BOOLEAN;
11
12 /*
13 Error handler used to stuff dummy VTable
14 entries. This is covered later.
15 */
16 void pure_virtual_called_error_handler();
17
18 /* Prototype graphics library function to draw a circle */
19 void glib_draw_circle (int x, int y, int radius);
20
21 typedef void (*VirtualFunctionPointer)(...);
22
23 /*
24 VTable structure used by the compiler to keep
25 track of the virtual functions associated with a class.
26 There is one instance of a VTable for every class
27 containing virtual functions. All instances of
28 a given class point to the same VTable.
29 */
30 struct VTable
31 {
32 /*
33 d and i fields are used when multiple inheritance and virtual
34 base classes are involved. We will be ignoring them for this
35 discussion.
36 */
37 int d;
38 int i;
39
40 /*
41 A function pointer to the virtual function to be called is
42 stored here.
43 */
44 VirtualFunctionPointer pFunc;
45 };
46
47 /*
48 The Shape class maps into the Shape structure in C. All
49 the member variables present in the class are included
50 as structure elements. Since Shape contains a virtual
51 function, a pointer to the VTable has also been added.
52 */
53
54 struct Shape
55 {
56 int m_x;
57 int m_y;
58
59 /*
60 The C++ compiler inserts an extra pointer to a vtable which
61 will keep a function pointer to the virtual function that
62 should be called.
63 */
64 VTable *pVTable;
65 };
66
67 /*
68 Function prototypes that correspond to the C++ methods
69 for the Shape class,
70 */
71 Shape *Shape_Constructor(Shape *this_ptr, int x, int y);
72 void Shape_Destructor(Shape *this_ptr, bool dynamic);
73 void Shape_MoveTo(Shape *this_ptr, int newX, int newY);
74 void Shape_Erase(Shape *this_ptr);
75
76 /*
77 The Shape vtable array contains entries for Draw and MoveTo
78 virtual functions. Notice that there is no entry for Erase,
79 as it is not virtual. Also, the first two fields for every
80 vtable entry are zero, these fields might have non zero
81 values with multiple inheritance, virtual base classes
82 A third entry has also been defined for the virtual destructor
83 */
84
85 VTable VTableArrayForShape[] =
86 {
87 /*
88 Vtable entry virtual function Draw.
89 Since Draw is pure virtual, this entry
90 should never be invoked, so call error handler
91 */
92 { 0, 0, (VirtualFunctionPointer) pure_virtual_called_error_handler },
93
94 /*
95 This vtable entry invokes the base class's
96 MoveTo method.
97 */
98 { 0, 0, (VirtualFunctionPointer) Shape_MoveTo },
99
100 /* Entry for the virtual destructor */
101 { 0, 0, (VirtualFunctionPointer) Shape_Destructor }
102 };
103
104 /*
105 The struct Circle maps to the Circle class in the C++ code.
106 The layout of the structure is:
107 - Member variables inherited from the the base class Shape.
108 - Vtable pointer for the class.
109 - Member variables added by the inheriting class Circle.
110 */
111
112 struct Circle
113 {
114 /* Fields inherited from Shape */
115 int m_x;
116 int m_y;
117 VTable *pVTable;
118
119 /* Fields added by Circle */
120 int m_radius;
121 };
122
123 /*
124 Function prototypes for methods in the Circle class.
125 */
126
127 Circle *Circle_Constructor(Circle *this_ptr, int x, int y, int radius);
128 void Circle_Draw(Circle *this_ptr);
129 void Circle_Destructor(Circle *this_ptr, BOOLEAN dynamic);
130
131 /* Vtable array for Circle */
132
133 VTable VTableArrayForCircle[] =
134 {
135 /*
136 Vtable entry virtual function Draw.
137 Circle_Draw method will be invoked when Shape's
138 Draw method is invoked
139 */
140 { 0, 0, (VirtualFunctionPointer) Circle_Draw },
141
142 /*
143 This vtable entry invokes the base class's
144 MoveTo method.
145 */
146 { 0, 0, (VirtualFunctionPointer) Shape_MoveTo },
147
148 /* Entry for the virtual destructor */
149 { 0, 0, (VirtualFunctionPointer) Circle_Destructor }
150 };
151
152 Shape *Shape_Constructor(Shape *this_ptr, int x, int y)
153 {
154 /* Check if memory has been allocated for struct Shape. */
155 if (this_ptr == NULL)
156 {
157 /* Allocate memory of size Shape. */
158 this_ptr = (Shape *) malloc(sizeof(Shape));
159 }
160
161 /*
162 Once the memory has been allocated for Shape,
163 initialise members of Shape.
164 */
165 if (this_ptr)
166 {
167 /* Initialize the VTable pointer to point to shape */
168 this_ptr->pVTable = VTableArrayForShape;
169 this_ptr->m_x = x;
170 this_ptr->m_y = y;
171 }
172
173 return this_ptr;
174 }
175
176 void Shape_Destructor(Shape *this_ptr, BOOLEAN dynamic)
177 {
178 /*
179 Restore the VTable to that for Shape. This is
180 required so that the destructor does not invoke
181 a virtual function defined by a inheriting class.
182 (The base class destructor is invoked after inheriting
183 class actions have been completed. Thus it is not
184 safe to invoke the ineriting class methods from the
185 base class destructor)
186 */
187 this_ptr->pVTable = VTableArrayForShape;
188
189 /*...*/
190
191 /*
192 If the memory was dynamically allocated
193 for Shape, explicitly free it.
194 */
195 if (dynamic)
196 {
197 free(this_ptr);
198 }
199 }
200
201 Circle *Circle_Constructor(Circle *this_ptr, int x, int y, int radius)
202 {
203 /* Check if memory has been allocated for struct Circle. */
204 if (this_ptr == NULL)
205 {
206 /* Allocate memory of size Circle. */
207 this_ptr = (Circle *) malloc(sizeof(Circle));
208 }
209
210 /*
211 Once the memory has been allocated for Circle,
212 initialise members of Circle.
213 */
214 if (this_ptr)
215 {
216 /* Invoking the base class constructor */
217 Shape_Constructor((Shape *)this_ptr, x, y);
218 this_ptr->pVTable = VTableArrayForCircle;
219
220 this_ptr->m_radius = radius;
221 }
222 return this_ptr;
223 }
224
225 void Circle_Destructor(Circle *this_ptr, BOOLEAN dynamic)
226 {
227 /* Restore the VTable to that for Circle */
228 this_ptr->pVTable = VTableArrayForCircle;
229
230 /*...*/
231
232 /*
233 Invoke the base class destructor after ineriting class
234 destructor actions have been completed. Also note that
235 that the dynamic flag is set to false so that the shape
236 destructor does not free any memory.
237 */
238 Shape_Destructor((Shape *) this_ptr, FALSE);
239
240 /*
241 If the memory was dynamically allocated
242 for Circle, explicitly free it.
243 */
244 if (dynamic)
245 {
246 free(this_ptr);
247 }
248 }
249
250 void Circle_Draw(Circle *this_ptr)
251 {
252 glib_draw_circle(this_ptr->m_x, this_ptr->m_y, this_ptr->m_radius);
253 }
254
255 main()
256 {
257 /*
258 Dynamically allocate memory by passing NULL in this arguement.
259 Also initialse members of struct pointed to by pShape.
260 */
261 Shape *pShape = (Shape *) Circle_Constructor(NULL, 50, 100, 25);
262
263 /* Define a local variable aCircle of type struct Circle. */
264 Circle aCircle;
265
266 /* Initialise members of struct variable aCircle. */
267 Circle_Constructor(&aCircle, 5, 5, 2);
268
269 /*
270 Virtual function Draw is called for the shape pointer. The compiler
271 has allocated 0 offset array entry to the Draw virtual function.
272 This code corresponds to "pShape->Draw();"
273 */
274 (pShape->pVTable[0].pFunc)(pShape);
275
276 /*
277 Virtual function MoveTo is called for the shape pointer. The compiler
278 has allocared 1 offset array entry to the MoveTo virtual function.
279 This code corresponds to "pShape->MoveTo(100, 100);"
280 */
281 (pShape->pVTable[1].pFunc)(pShape, 100, 100);
282
283 /*
284 The following code represents the Erase method. This method is
285 not virtual and it is only defined in the base class. Thus
286 the Shape_Erase C function is called.
287 */
288 Shape_Erase(pShape);
289
290 /* Delete memory pointed to by pShape (explicit delete in original code).
291 Since the destructor is declared virtual, the compiler has allocated
292 2 offset entry to the virtual destructor
293 This code corresponds to "delete pShape;".
294 */
295 (pShape->pVTable[2].pFunc)(pShape, TRUE);
296
297 /*
298 The following code corresponds to aCircle.Draw().
299 Here the compiler can invoke the method directly instead of
300 going through the vtable, since the type of aCircle is fully
301 known. (This is very much compiler dependent. Dumb compilers will
302 still invoke the method through the vtable).
303 */
304 Circle_Draw(&aCircle);
305
306 /*
307 Since memory was allocated from the stack for local struct
308 variable aCircle, it will be deallocated when aCircle goes out of scope.
309 The destructor will also be invoked. Notice that dynamic flag is set to
310 false so that the destructor does not try to free memory. Again, the
311 compiler does not need to go through the vtable to invoke the destructor.
312 */
313 Circle_Destructor(&aCircle, FALSE);
314 }