C语言实现C++的继承和多态

C++语言的对象化模型

.封装,隐藏内部实现

.继承,复用现有代码

.多态,改写对象行为

本文描述了一个C++实现的继承和多态的场景,然后用C语言编写了一种对等的实现。


// A typical example of inheritance and virtual function use.
// We would be mapping this code to equivalent C.

// Prototype graphics library function to draw a circle
void glib_draw_circle (int x, int y, int radius);

// Shape base class declaration
class Shape
{
protected:
int m_x; // X coordinate
int m_y; // Y coordinate

public:
// Pure virtual function for drawing
virtual void Draw() = 0;

// A regular virtual function
virtual void MoveTo(int newX, int newY);

// Regular method, not overridable.
void Erase();

// Constructor for Shape
Shape(int x, int y);

// Virtual destructor for Shape
virtual ~Shape();
};

// Circle class declaration
class Circle : public Shape
{
private:
int m_radius; // Radius of the circle

public:
// Override to draw a circle
virtual void Draw();

// Constructor for Circle
Circle(int x, int y, int radius);

// Destructor for Circle
virtual ~Circle();
};

// Shape constructor implementation
Shape::Shape(int x, int y)
{
m_x = x;
m_y = y;
}

// Shape destructor implementation
Shape::~Shape()
{
//...
}

// Circle constructor implementation
Circle::Circle(int x, int y, int radius) : Shape (x, y)
{
m_radius = radius;
}

// Circle destructor implementation
Circle::~Circle()
{
//...
}

// Circle override of the pure virtual Draw method.
void Circle::Draw()
{
glib_draw_circle(m_x, m_y, m_radius);
}

main()
{
// Define a circle with a center at (50,100) and a radius of 25
Shape *pShape = new Circle(50, 100, 25);

// Define a circle with a center at (5,5) and a radius of 2
Circle aCircle(5,5, 2);

// Various operations on a Circle via a Shape pointer
pShape->Draw();
pShape->MoveTo(100, 100);
pShape->Erase();
delete pShape;

// Invoking the Draw method directly
aCircle.Draw();
}

C语言实现:


/*
The following code maps the C++ code for the Shape and Circle classes
to C code.
*/

#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
typedef int BOOLEAN;

/*
Error handler used to stuff dummy VTable
entries. This is covered later.
*/
void pure_virtual_called_error_handler();

/* Prototype graphics library function to draw a circle */
void glib_draw_circle (int x, int y, int radius);

typedef void (*VirtualFunctionPointer)(...);

/*
VTable structure used by the compiler to keep
track of the virtual functions associated with a class.
There is one instance of a VTable for every class
containing virtual functions. All instances of
a given class point to the same VTable.
*/
struct VTable
{
/*
d and i fields are used when multiple inheritance and virtual
base classes are involved. We will be ignoring them for this
discussion.
*/
int d;
int i;

/*
A function pointer to the virtual function to be called is
stored here.
*/
VirtualFunctionPointer pFunc;
};

/*
The Shape class maps into the Shape structure in C. All
the member variables present in the class are included
as structure elements. Since Shape contains a virtual
function, a pointer to the VTable has also been added.
*/

struct Shape
{
int m_x;
int m_y;

/*
The C++ compiler inserts an extra pointer to a vtable which
will keep a function pointer to the virtual function that
should be called.
*/
VTable *pVTable;
};

/*
Function prototypes that correspond to the C++ methods
for the Shape class,
*/
Shape *Shape_Constructor(Shape *this_ptr, int x, int y);
void Shape_Destructor(Shape *this_ptr, bool dynamic);
void Shape_MoveTo(Shape *this_ptr, int newX, int newY);
void Shape_Erase(Shape *this_ptr);

/*
The Shape vtable array contains entries for Draw and MoveTo
virtual functions. Notice that there is no entry for Erase,
as it is not virtual. Also, the first two fields for every
vtable entry are zero, these fields might have non zero
values with multiple inheritance, virtual base classes
A third entry has also been defined for the virtual destructor
*/

VTable VTableArrayForShape[] =
{
/*
Vtable entry virtual function Draw.
Since Draw is pure virtual, this entry
should never be invoked, so call error handler
*/
{ 0, 0, (VirtualFunctionPointer) pure_virtual_called_error_handler },

/*
This vtable entry invokes the base class‘s
MoveTo method.
*/
{ 0, 0, (VirtualFunctionPointer) Shape_MoveTo },

/* Entry for the virtual destructor */
{ 0, 0, (VirtualFunctionPointer) Shape_Destructor }
};

/*
The struct Circle maps to the Circle class in the C++ code.
The layout of the structure is:
- Member variables inherited from the the base class Shape.
- Vtable pointer for the class.
- Member variables added by the inheriting class Circle.
*/

struct Circle
{
/* Fields inherited from Shape */
int m_x;
int m_y;
VTable *pVTable;

/* Fields added by Circle */
int m_radius;
};

/*
Function prototypes for methods in the Circle class.
*/

Circle *Circle_Constructor(Circle *this_ptr, int x, int y, int radius);
void Circle_Draw(Circle *this_ptr);
void Circle_Destructor(Circle *this_ptr, BOOLEAN dynamic);

/* Vtable array for Circle */

VTable VTableArrayForCircle[] =
{
/*
Vtable entry virtual function Draw.
Circle_Draw method will be invoked when Shape‘s
Draw method is invoked
*/
{ 0, 0, (VirtualFunctionPointer) Circle_Draw },

/*
This vtable entry invokes the base class‘s
MoveTo method.
*/
{ 0, 0, (VirtualFunctionPointer) Shape_MoveTo },

/* Entry for the virtual destructor */
{ 0, 0, (VirtualFunctionPointer) Circle_Destructor }
};

Shape *Shape_Constructor(Shape *this_ptr, int x, int y)
{
/* Check if memory has been allocated for struct Shape. */
if (this_ptr == NULL)
{
/* Allocate memory of size Shape. */
this_ptr = (Shape *) malloc(sizeof(Shape));
}

/*
Once the memory has been allocated for Shape,
initialise members of Shape.
*/
if (this_ptr)
{
/* Initialize the VTable pointer to point to shape */
this_ptr->pVTable = VTableArrayForShape;
this_ptr->m_x = x;
this_ptr->m_y = y;
}

return this_ptr;
}

void Shape_Destructor(Shape *this_ptr, BOOLEAN dynamic)
{
/*
Restore the VTable to that for Shape. This is
required so that the destructor does not invoke
a virtual function defined by a inheriting class.
(The base class destructor is invoked after inheriting
class actions have been completed. Thus it is not
safe to invoke the ineriting class methods from the
base class destructor)
*/
this_ptr->pVTable = VTableArrayForShape;

/*...*/

/*
If the memory was dynamically allocated
for Shape, explicitly free it.
*/
if (dynamic)
{
free(this_ptr);
}
}

Circle *Circle_Constructor(Circle *this_ptr, int x, int y, int radius)
{
/* Check if memory has been allocated for struct Circle. */
if (this_ptr == NULL)
{
/* Allocate memory of size Circle. */
this_ptr = (Circle *) malloc(sizeof(Circle));
}

/*
Once the memory has been allocated for Circle,
initialise members of Circle.
*/
if (this_ptr)
{
/* Invoking the base class constructor */
Shape_Constructor((Shape *)this_ptr, x, y);
this_ptr->pVTable = VTableArrayForCircle;

this_ptr->m_radius = radius;
}
return this_ptr;
}

void Circle_Destructor(Circle *this_ptr, BOOLEAN dynamic)
{
/* Restore the VTable to that for Circle */
this_ptr->pVTable = VTableArrayForCircle;

/*...*/

/*
Invoke the base class destructor after ineriting class
destructor actions have been completed. Also note that
that the dynamic flag is set to false so that the shape
destructor does not free any memory.
*/
Shape_Destructor((Shape *) this_ptr, FALSE);

/*
If the memory was dynamically allocated
for Circle, explicitly free it.
*/
if (dynamic)
{
free(this_ptr);
}
}

void Circle_Draw(Circle *this_ptr)
{
glib_draw_circle(this_ptr->m_x, this_ptr->m_y, this_ptr->m_radius);
}

main()
{
/*
Dynamically allocate memory by passing NULL in this arguement.
Also initialse members of struct pointed to by pShape.
*/
Shape *pShape = (Shape *) Circle_Constructor(NULL, 50, 100, 25);

/* Define a local variable aCircle of type struct Circle. */
Circle aCircle;

/* Initialise members of struct variable aCircle. */
Circle_Constructor(&aCircle, 5, 5, 2);

/*
Virtual function Draw is called for the shape pointer. The compiler
has allocated 0 offset array entry to the Draw virtual function.
This code corresponds to "pShape->Draw();"
*/
(pShape->pVTable[0].pFunc)(pShape);

/*
Virtual function MoveTo is called for the shape pointer. The compiler
has allocared 1 offset array entry to the MoveTo virtual function.
This code corresponds to "pShape->MoveTo(100, 100);"
*/
(pShape->pVTable[1].pFunc)(pShape, 100, 100);

/*
The following code represents the Erase method. This method is
not virtual and it is only defined in the base class. Thus
the Shape_Erase C function is called.
*/
Shape_Erase(pShape);

/* Delete memory pointed to by pShape (explicit delete in original code).
Since the destructor is declared virtual, the compiler has allocated
2 offset entry to the virtual destructor
This code corresponds to "delete pShape;".
*/
(pShape->pVTable[2].pFunc)(pShape, TRUE);

/*
The following code corresponds to aCircle.Draw().
Here the compiler can invoke the method directly instead of
going through the vtable, since the type of aCircle is fully
known. (This is very much compiler dependent. Dumb compilers will
still invoke the method through the vtable).
*/
Circle_Draw(&aCircle);

/*
Since memory was allocated from the stack for local struct
variable aCircle, it will be deallocated when aCircle goes out of scope.
The destructor will also be invoked. Notice that dynamic flag is set to
false so that the destructor does not try to free memory. Again, the
compiler does not need to go through the vtable to invoke the destructor.
*/
Circle_Destructor(&aCircle, FALSE);
}

参照以上代码,可以总结出用C语言实现C++的对象模型的方法:

1、对于成员变量使用struct实现,对于成员方法采用普通函数+struct指针参数的方式来实现对成员变量的操作和对其他成员方法的调用。

2、继承,采用拷贝父类成员变量为子类成员变量的方式来实现。

3、多态,为struct添加一个函数表指针,为每个类型构建一个函数表(存储的为函数指针),实例通过对应函数表中的函数指针来完成多态函数的调用。

参考资料:

用C来实现C++中的继承和虚函数特性

http://www.eventhelix.com/realtimemantra/basics/ComparingCPPAndCPerformance2.htm

C语言实现C++的继承和多态,布布扣,bubuko.com

时间: 2024-10-07 06:29:04

C语言实现C++的继承和多态的相关文章

c语言实现封装、继承和多态

1.  概述 C语言是一种面向过程的程序设计语言,而C++是在C语言基础上衍生来了的面向对象的语言,实际上,很多C++实现的底层是用C语言实现的,如在Visual C++中的Interface其实就是struct,查找Interface的定义,你可以发现有这样的宏定义: #ifndef Interface #define Interface struct #endif C++在语言级别上添加了很多新机制(继承,多态等),而在C语言中,我们也可以使用这样的机制,前提是我们不得不自己实现. 本文介绍

c语言中继承和多态的简单实现

C语言本身是不支持继承和多态的,但其实在 C 的世界里,有一套非常有名的面向对象的框架,用的也非常广,那就是 GObject,它是整个图形界面开发库 GTK 的基石,在IBM developerWorks上有一篇很好的文章介绍 GObject<GObject对象系统>.另外,在 Linux 内核里面也大量使用了面向对象的思想,比如虚拟文件系统,设备驱动等模块,在 lwn上有两篇文章就讲到了内核中的面向对象,详细请看:<Object oriented design patterns in

2、C#面向对象:封装、继承、多态、String、集合、文件(上)

面向对象封装 一.面向对象概念 面向过程:面向的是完成一件事情的过程,强调的是完成这件事情的动作. 面向对象:找个对象帮你完成这件事情. 二.面向对象封装 把方法进行封装,隐藏实现细节,外部直接调用. 打包,便于管理,为了解决大型项目的维护与管理. 三.什么是类? 将相同的属性和相同方法的对象进行封装,抽象出 “类”,用来确定对象具有的属性和方法. 类.对象关系:人是类,张三是人类的对象. 类是抽象的,对象是具体的.对象可以叫做类的实例,类是不站内存的,对象才占内存. 字段是类的状态,方法是类执

Java 继承、多态与类的复用

摘要: 本文结合Java的类的复用对面向对象两大特征继承和多态进行了全面的介绍. 首先,我们介绍了继承的实质和意义,并探讨了继承,组合和代理在类的复用方面的异同.紧接着,我们依据继承引入了多态.介绍了它的实现机制和详细应用.此外,为了更好地理解继承和多态.我们对final关键字进行了全面的介绍. 在此基础上.我们介绍了Java中类的载入及初始化顺序.最后.我们对面向对象设计中三个十分重要的概念–重载.覆盖与隐藏进行了详细的说明. 要点: 继承 组合,继承,代理 多态 final 关键字 类载入及

C#语言基础知识(2):C#中多态

我的理解是:通过继承实现的不同对象调用相同的方法,表现出不同的行为,称之为多态. 1: OverRide 实现多态 1 public class Animal 2 { 3 public virtual void Eat() 4 { 5 Console.WriteLine("Animal eat"); 6 } 7 } 8 public class Dog : Animal 9 { 10 public override void Eat() 11 { 12 Console.WriteLin

学习java随笔第八篇:封装、继承、多态

java和c#一样都是面向对象的语言. 面向对象的语言有三大特征:封装.继承.多态 封装 封装:隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别. class Person2 { private String name; public void setName(String name) { this.name=name; } public String getName() { return name; } private String sex; public voi

OOP三个基本特征:封装、继承、多态

面向对象的三个基本特征是:封装.继承.多态. 封装 封装最好理解了.封装是面向对象的特征之一,是对象和类概念的主要特性. 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏. 继承 面向对象编程 (OOP) 语言的一个主要功能就是“继承”.继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展. 继承现有类 + 扩展 通过继承创建的新类称为“子类”或“派生类”. 被继承的类称为“基类”.

类和对象、封装、继承、多态

? 类和对象 ? 类和对象的概念 类:客观存在的,抽象的,概念的东西.一个类可以被定义为描述行为的模板: 对象:对象是具体的,实际的,代表一个事物.对象具有状态和行为. 类是对象的模板,对象是类的一个具体实体. 定义Java中的类 一个类可以包含以下任意变量类型. a)局部变量: 方法里面,构造函数或块中定义的变量称为局部变量.该变量将被声明和初始化的方法中,当该方法完成该变量将被销毁. b)实例变量: 实例变量都在一个类,但任何方法之外的变量.这些变量在类被加载的实例化.实例变量可以从内部的任

[转]Java中继承、多态、重载和重写介绍

什么是多态?它的实现机制是什么呢?重载和重写的区别在那里?这就是这一次我们要回顾的四个十分重要的概念:继承.多态.重载和重写. 继承(inheritance) 简单的说,继承就是在一个现有类型的基础上,通过增加新的方法或者重定义已有方法(下面会讲到,这种方式叫重写)的方式,产生一个新的类型.继承是面向对象的三个基本特征--封装.继承.多态的其中之一,我们在使用JAVA时编写的每一个类都是在继承,因为在JAVA语言中,java.lang.Object类是所有类最根本的基类(或者叫父类.超类),如果