繼承和多態是面嚮對象語言最強大的功能。有了繼承和多態,咱們能夠完成代碼重用。在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