C語言實現繼承和多態(感受有點誤導)

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

}

Employee對象的結構

注意:從基類函數到派生類函數改變了接口(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

相關文章
相關標籤/搜索