继承和多态是面向对象语言最强大的功能。有了继承和多态,咱们能够完成代码重用。在C中有许多技巧能够实现多态。本文的目的就是演示一种简单和容易的技术,在C中应用继承和多态。经过建立一个VTable(virtual table)和在基类和派生类对象之间提供正确的访问,咱们能在C中实现继承和多态。VTable能经过维护一张函数表指针表来实现。为了提供基类和派生类对象之间的访问,咱们能够在基类中维护派生类的引用和在派生类中维护基类的引用。ide
二、说明函数
在C中实现继承和多态以前,咱们应该知道类(Class)在C中如何表示。this
2.一、类在C中的表示spa
考虑C++中的一个类"Person"。.net
//Person.h3d
class Person指针
{对象
private:blog
char* pFirstName;继承
char* pLastName;
public:
Person(const char* pFirstName, const char* pLastName); //constructor
~Person(); //destructor
void displayInfo();
void writeToFile(const char* pFileName);
};
在C中表示上面的类,咱们可使用结构体,并用操做结构体的函数表示成员函数。
//Person.h
typedef struct _Person
{
char* pFirstName;
char* pLastName;
}Person;
new_Person(const char* const pFirstName, const char* const pLastName); //constructor
delete_Person(Person* const pPersonObj); //destructor
void Person_DisplayInfo(Person* const pPersonObj);
void Person_WriteToFile(Person* const pPersonObj, const char* const pFileName);
这里,定义的操做结构体Person的函数没有封装。为了实现封装,即绑定数据、函数、函数指针。咱们须要建立一个函数指针表。构造函数new_Person()将设置函数指针值以指向合适的函数。这个函数指针表将做为对象访问函数的接口。
下面咱们从新定义C中实现类Person。
//Person.h
typedef struct _Person Person;
//declaration of pointers to functions
typedef void (*fptrDisplayInfo)(Person*);
typedef void (*fptrWriteToFile)( Person*, const char*);
typedef void (*fptrDelete)( Person *) ;
//Note: In C all the members are by default public. We can achieve
//the data hiding (private members), but that method is tricky.
//For simplification of this article
// we are considering the data members //public only.
typedef struct _Person
{
char* pFName;
char* pLName;
//interface for function
fptrDisplayInfo Display;
fptrWriteToFile WriteToFile;
fptrDelete Delete;
}Person;
person* new_Person(const char* const pFirstName,
const char* const pLastName); //constructor
void delete_Person(Person* const pPersonObj); //destructor
void Person_DisplayInfo(Person* const pPersonObj);
void Person_WriteToFile(Person* const pPersonObj, const char* pFileName);
new_Person()函数做为构造函数,它返回新建立的结构体实例。它初始化函数指针接口去访问其它成员函数。这里要注意的一点是,咱们仅仅定义了那些容许公共访问的函数指针,并无给定私有函数的接口。让咱们看一下new_Person()函数或C中类Person的构造函数。
//Person.c
person* new_Person(const char* const pFirstName, const char* const pLastName)
{
Person* pObj = NULL;
//allocating memory
pObj = (Person*)malloc(sizeof(Person));
if (pObj == NULL)
{
return NULL;
}
pObj->pFirstName = malloc(sizeof(char)*(strlen(pFirstName)+1));
if (pObj->pFirstName == NULL)
{
return NULL;
}
strcpy(pObj->pFirstName, pFirstName);
pObj->pLastName = malloc(sizeof(char)*(strlen(pLastName)+1));
if (pObj->pLastName == NULL)
{
return NULL;
}
strcpy(pObj->pLastName, pLastName);
//Initializing interface for access to functions
pObj->Delete = delete_Person;
pObj->Display = Person_DisplayInfo;
pObj->WriteToFile = Person_WriteToFile;
return pObj;
}
建立完对象以后,咱们可以访问它的数据成员和函数。
Person* pPersonObj = new_Person("Anjali", "Jaiswal");
//displaying person info
pPersonObj->Display(pPersonObj);
//writing person info in the persondata.txt file
pPersonObj->WriteToFile(pPersonObj, "persondata.txt");
//delete the person object
pPersonObj->Delete(pPersonObj);
pPersonObj = NULL;
注意:不像C++,在C中咱们不能在函数中直接访问数据成员。在C++中,能够隐式经过“this”指针直接访问数据成员。咱们知道C中是没有“this”指针的,经过显示地传递对象给成员函数。在C中为了访问类的数据成员,咱们须要把调用对象做为函数参数传递。上面的例子中,咱们把调用对象做为函数的第一个参数,经过这种方法,函数能够访问对象的数据成员。
三、在C中类的表现
Person类的表示——检查初始化接口指向成员函数:
3.一、继承和多态的简单例子
继承-Employee类继承自Person类:
在上面的例子中,类Employee继承类Person的属性。由于DisplayInfo()和WriteToFile()函数是virtual的,咱们可以从Person的实例访问Employee对象中的同名函数。为了实现这个,咱们建立Person实例的时候也初始化Employee类。多态使这成为可能。 在多态的状况下,去解析函数调用,C++使用VTable——即一张函数指针表。
前面咱们在结构体中维护的指向函数的指针接口的做用相似于VTable。
//Polymorphism in C++
Person PersonObj("Anjali", "Jaiswal");
Employee EmployeeObj("Gauri", "Jaiswal", "HR", "TCS", 40000);
Person* ptrPersonObj = NULL;
//preson pointer pointing to person object
ptrPersonObj = &PersonObj;
//displaying person info
ptrPersonObj ->Display();
//writing person info in the persondata.txt file
ptrPersonObj ->WriteToFile("persondata.txt");
//preson pointer pointing to employee object
ptrPersonObj = &EmployeeObj;
//displaying employee info
ptrPersonObj ->Display();
//writing empolyee info in the employeedata.txt file
ptrPersonObj ->WriteToFile("employeedata.txt");
在C中,继承能够经过在派生类对象中维护一个基类对象的引用来完成。在基类实例的帮助下,women能够访问基类的数据成员和函数。然而,为了实现多态,街垒对象应该可以访问派生类对象的数据。为了实现这个,基类应该有访问派生类的数据成员的权限。
为了实现虚函数,派生类的函数签名应该和基类的函数指针相似。即派生类函数将以基类对象的一个实例为参数。咱们在基类中维护一个派生类的引用。在函数实现上,咱们能够从派生类的引用访问实际派生类的数据。
3.二、在C中结构体中的等效表示
C中的继承-Person和Employee结构体:
如图所示,咱们在基类结构体中声明了一个指针保存派生类对像,并在派生类结构体中声明一个指针保存基类对象。
在基类对象中,函数指针指向本身的虚函数。在派生类对象的构造函数中,咱们须要使基类的接口指向派生类的成员函数。这使咱们能够经过基类对象(多态)灵活的调用派生类函数。更多细节,请检查Person和Employee对象的构造函数。
当咱们讨论C++中的多态时,有一个对象销毁的问题。为了正确的清楚对象,它使用虚析构函数。在C中,这能够经过使基类的删除函数指针指向派生类的析构函数。派生类的析构函数清楚派生类的数据和基类的数据和对象。注意:检查例子的源码中,实现须构造函数和虚函数的实现细节。
建立Person对象
//Person.h
typedef struct _Person Person;
//pointers to function
typedef void (*fptrDisplayInfo)(Person*);
typedef void (*fptrWriteToFile)(Person*, const char*);
typedef void (*fptrDelete)(Person*) ;
typedef struct _person
{
void* pDerivedObj;
char* pFirstName;
char* pLastName;
fptrDisplayInfo Display;
fptrWriteToFile WriteToFile;
fptrDelete Delete;
}person;
Person* new_Person(const char* const pFristName,
const char* const pLastName); //constructor
void delete_Person(Person* const pPersonObj); //destructor
void Person_DisplayInfo(Person* const pPersonObj);
void Person_WriteToFile(Person* const pPersonObj, const char* const pFileName);
//Person.c
//construction of Person object
Person* new_Person(const char* const pFirstName, const char* const pLastName)
{
Person* pObj = NULL;
//allocating memory
pObj = (Person*)malloc(sizeof(Person));
if (pObj == NULL)
{
return NULL;
}
//pointing to itself as we are creating base class object
pObj->pDerivedObj = pObj;
pObj->pFirstName = malloc(sizeof(char)*(strlen(pFirstName)+1));
if (pObj->pFirstName == NULL)
{
return NULL;
}
strcpy(pObj->pFirstName, pFirstName);
pObj->pLastName = malloc(sizeof(char)*(strlen(pLastName)+1));
if (pObj->pLastName == NULL)
{
return NULL;
}
strcpy(pObj->pLastName, pLastName);
//Initializing interface for access to functions
//destructor pointing to destrutor of itself
pObj->Delete = delete_Person;
pObj->Display = Person_DisplayInfo;
pObj->WriteToFile = Person_WriteToFile;
return pObj;
}
Person对象的结构
建立Employee对象
//Employee.h
#include "Person.h"
typedef struct _Employee Employee;
//Note: interface for this class is in the base class
//object since all functions are virtual.
//If there is any additional functions in employee add
//interface for those functions in this structure
typedef struct _Employee
{
Person* pBaseObj;
char* pDepartment;
char* pCompany;
int nSalary;
//If there is any employee specific functions; add interface here.
}Employee;
Person* new_Employee(const char* const pFirstName, const char* const pLastName,
const char* const pDepartment, const char* const pCompany,
int nSalary); //constructor
void delete_Employee(Person* const pPersonObj); //destructor
void Employee_DisplayInfo(Person* const pPersonObj);
void Employee_WriteToFile(Person* const pPersonObj, const char* const pFileName);
//Employee.c
Person* new_Employee(const char* const pFirstName, const char* const pLastName,
const char* const pDepartment,
const char* const pCompany, int nSalary)
{
Employee* pEmpObj;
//calling base class construtor
Person* pObj = new_Person(pFirstName, pLastName);
//allocating memory
pEmpObj = malloc(sizeof(Employee));
if (pEmpObj == NULL)
{
pObj->Delete(pObj);
return NULL;
}
pObj->pDerivedObj = pEmpObj; //pointing to derived object
//initialising derived class members
pEmpObj->pDepartment = malloc(sizeof(char)*(strlen(pDepartment)+1));
if(pEmpObj->pDepartment == NULL)
{
return NULL;
}
strcpy(pEmpObj->pDepartment, pDepartment);
pEmpObj->pCompany = malloc(sizeof(char)*(strlen(pCompany)+1));
if(pEmpObj->pCompany== NULL)
{
return NULL;
}
strcpy(pEmpObj->pCompany, pCompany);
pEmpObj->nSalary = nSalary;
//Changing base class interface to access derived class functions
//virtual destructor
//person destructor pointing to destrutor of employee
pObj->Delete = delete_Employee;
pObj->Display = Employee_DisplayInfo;
pObj->WriteToFile = Employee_WriteToFile;
return pObj;
}
注意:从基类函数到派生类函数改变了接口(VTable)中指针位置。如今咱们能够从基类(多态)访问派生类函数。咱们来看如何使用多态。
Person* PersonObj = new_Person("Anjali", "Jaiswal");
Person* EmployeeObj = new_Employee("Gauri", "Jaiswal","HR", "TCS", 40000);
//accessing person object
//displaying person info
PersonObj->Display(PersonObj);
//writing person info in the persondata.txt file
PersonObj->WriteToFile(PersonObj,"persondata.txt");
//calling destructor
PersonObj->Delete(PersonObj);
//accessing to employee object
//displaying employee info
EmployeeObj->Display(EmployeeObj);
//writing empolyee info in the employeedata.txt file
EmployeeObj->WriteToFile(EmployeeObj, "employeedata.txt");
//calling destrutor
EmployeeObj->Delete(EmployeeObj);
结论
使用上面描述的简单的额外代码能是过程式C语言有多态和继承的特性。咱们简单的使用函数指针建立一个VTable和在基类和派生类对象中交叉维护引用。用这些简单的步骤,咱们在C中能够实现继承和多态。
https://blog.csdn.net/changyourmind/article/details/52199898