• 正文
  • 推薦器件
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

嵌入式 C 語(yǔ)言面向?qū)ο缶幊?--- 繼承

2024/07/09
1262
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論
我是老溫,一名熱愛(ài)學(xué)習(xí)的嵌入式工程師,關(guān)注我,一起變得更加優(yōu)秀!

上一篇文章主要講述了 C 語(yǔ)言面向?qū)ο缶幊台C 封裝的簡(jiǎn)單概念和實(shí)現(xiàn),本篇文章繼續(xù)來(lái)討論一下,如何使用 C 語(yǔ)言實(shí)現(xiàn)面向?qū)ο缶幊痰牧硪粋€(gè)重要特性:繼承。

繼承就是基于一個(gè)已有的類(lèi)(一般稱(chēng)作父類(lèi)或基類(lèi)),再去重新聲明或創(chuàng)建一個(gè)新的類(lèi),這個(gè)類(lèi)可以稱(chēng)為子類(lèi)或派生類(lèi)。

子類(lèi)或派生類(lèi)可以訪問(wèn)父類(lèi)的數(shù)據(jù)和函數(shù),然后子類(lèi)里面又添加了自己的屬性和數(shù)據(jù)。

簡(jiǎn)單打個(gè)比喻就是,眼睛耳朵嘴巴鼻子這幾項(xiàng)都是動(dòng)物的特征,鄰居老王家的小狗旺財(cái)繼承了動(dòng)物的特征,并且還有自身的一些特征,比如:會(huì)擺動(dòng)的尾巴。也就是,旺財(cái)這個(gè)dog類(lèi),繼承了其父類(lèi)animal類(lèi)的特征,并且還新增加了屬于自身的一些特征。

一句話高度概括什么是面向?qū)ο螅?strong>到底是旺財(cái)自己會(huì)搖尾巴,還是外部的因素驅(qū)使旺財(cái)會(huì)搖尾巴?

仔細(xì)斟酌這句話,當(dāng)把這句話理解透了,就會(huì)對(duì)“面向?qū)ο蟆钡母拍钣懈由羁痰睦斫狻?/p>

在 C 語(yǔ)言里面,可以通過(guò)結(jié)構(gòu)體嵌套的方式去實(shí)現(xiàn)類(lèi)的單繼承(暫不考慮多重繼承),但有一點(diǎn)注意事項(xiàng),就是在結(jié)構(gòu)體嵌套時(shí),父類(lèi)對(duì)象需要放在結(jié)構(gòu)體成員的第一個(gè)位置。

現(xiàn)在,我們基于已有的 coordinate 類(lèi)作為父類(lèi),再重新定義一個(gè) rectangle 派生類(lèi)。

在上一篇文章代碼的基礎(chǔ)上,我們修改一下父類(lèi) coordinate,把操作函數(shù)通過(guò)函數(shù)指針的方式封裝在結(jié)構(gòu)體內(nèi),讓對(duì)象的封裝程度進(jìn)一步提高。

修改后的父類(lèi)coordinate代碼,如下所示:

#ifndef __COORDINATE_H_
#define __COORDINATE_H_

//聲明一個(gè)位置類(lèi),屬性為坐標(biāo)x,y,提供屬性操作函數(shù)
typedef struct coordinate {
    short int x;
    short int y;
    void (*moveby)(struct coordinate *p_coordinate,short int dx,short int dy);
    short int (*get_x)(struct coordinate *p_coordinate);
    short int (*get_y)(struct coordinate *p_coordinate);
}COORDINATE_T,*P_COORDINATE_T;

extern void coordinate_init(P_COORDINATE_T p_coordinate,short int x,short int y);
extern void coordinate_uninit(P_COORDINATE_T p_coordinate);

#endif // !__COORDINATE_H_

在頭文件 coordinate.h 里,聲明一個(gè)位置類(lèi),類(lèi)里面提供了坐標(biāo)屬性 x 和 y,還提供了屬性的操作函數(shù)指針。頭文件對(duì)外提供 coordinate_init 和 coordinate_uninit 兩個(gè)函數(shù),用來(lái)初始化對(duì)象和解除初始化。

源文件 coordinate.c

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "inc/coordinate.h"

//修改coordinate的屬性值
static void coordinate_moveby(struct coordinate *p_coordiante,short int dx,short int dy)
{
    if(NULL != p_coordiante){
        p_coordiante->x += dx;
        p_coordiante->y += dy;
    }
}

//獲取coordinate的屬性值x
static short int coordinate_get_x(struct coordinate *p_coordiante)
{
    return (NULL != p_coordiante) ? p_coordiante->x : -1;
}

//獲取coordinate的屬性值y
static short int coordinate_get_y(struct coordinate *p_coordiante)
{
    return (NULL != p_coordiante) ? p_coordiante->y : -1;
}

//創(chuàng)建一個(gè)coordinate對(duì)象
void coordinate_init(P_COORDINATE_T p_coordinate,short int x,short int y)
{
    if((x < 0) || (y < 0) || (NULL == p_coordinate)){
        printf("coordinate create error! x or y can not be less than zero n");
        return;
    }

    p_coordinate->x = x;
    p_coordinate->y = y;
    p_coordinate->moveby = coordinate_moveby;
    p_coordinate->get_x = coordinate_get_x;
    p_coordinate->get_y = coordinate_get_y;
}

//銷(xiāo)毀一個(gè)coordinate對(duì)象
void coordinate_uninit(P_COORDINATE_T p_coordinate)
{
    if(NULL != p_coordinate){
        p_coordinate->x = -1;
        p_coordinate->y = -1;
        p_coordinate->moveby = NULL;
        p_coordinate->get_x = NULL;
        p_coordinate->get_y = NULL;
    }
}

在源文件 coordinate.c 里,屬性的操作函數(shù)都使用 static 進(jìn)行聲明,只能在該源文件調(diào)用函數(shù),不允許外部調(diào)用。在函數(shù) coordinate_init 中,主要進(jìn)行了屬性賦值,并注冊(cè)操作函數(shù)指針,后面可以直接通過(guò)函數(shù)指針對(duì)操作函數(shù)進(jìn)行調(diào)用。在函數(shù) coordinate_uninit 中,主要是清除各個(gè)屬性的賦值。

至此,整個(gè)父類(lèi) coordinate 修改完成,父類(lèi)把屬性和屬性的操作函數(shù)都封裝在結(jié)構(gòu)體內(nèi),其封裝程度已經(jīng)比較高,外部不能直接調(diào)用父類(lèi)的屬性操作函數(shù),必須通過(guò)函數(shù)指針的方式進(jìn)行調(diào)用。

接下來(lái),我們基于父類(lèi) coordinate ,重新聲明一個(gè)子類(lèi) rectangle ,子類(lèi)在頭文件中的聲明,如下所示:

頭文件 rectangle.h

#ifndef __RECTANGLE_H_
#define __RECTANGLE_H_
#include "coordinate.h"    //包含基類(lèi)的接口

//聲明一個(gè)rectangle類(lèi),繼承coordinate類(lèi)
typedef struct rectangle {
    COORDINATE_T coordinate; //父類(lèi),必須放在首位
    unsigned short width;
    unsigned short height;
}RECTANGLE_T,*P_RECTANGLE_T;

extern P_RECTANGLE_T rectangle_create(short int x,short int y,unsigned short width,unsigned short height);
extern void rectangle_destroy(P_RECTANGLE_T p_rectangle);
extern void rectangle_test_function(void);

#endif // !__RECTANGLE_H_

源文件 rectangle.c

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "inc/rectangle.h"

//創(chuàng)建一個(gè)rectangle類(lèi)對(duì)象
P_RECTANGLE_T rectangle_create(short int x,short int y,unsigned short width,unsigned short height)
{
    P_RECTANGLE_T p_rectangle = NULL;

    p_rectangle = (P_RECTANGLE_T)malloc(sizeof(RECTANGLE_T));

    if(NULL != p_rectangle){       
        p_rectangle->width = width;
        p_rectangle->height = height;

        coordinate_init(&(p_rectangle->coordinate),x,y);
    }
    else printf("rectangle create error! n");
    return p_rectangle;
}

//銷(xiāo)毀一個(gè)rectangle類(lèi)對(duì)象
void rectangle_destroy(P_RECTANGLE_T p_rectangle)
{
    coordinate_uninit(&(p_rectangle->coordinate));

    if(NULL != p_rectangle){
        free(p_rectangle);
        p_rectangle = NULL;
    }
}

在頭文件 rectangle.h 里面,通過(guò)include包含了父類(lèi)coordinate的接口,并創(chuàng)建了一個(gè)新的結(jié)構(gòu)體,用于聲明一個(gè) rectangle 類(lèi),這個(gè)結(jié)構(gòu)體把父類(lèi) coordinate 放在了第一個(gè)成員的位置,同時(shí)新增了自己的兩個(gè)屬性,寬度width和高度height。

rectangle_create 函數(shù)用于創(chuàng)建一個(gè) P_RECTANGLE_T 類(lèi)型的對(duì)象,并為其分配內(nèi)存空間。分配成功后,對(duì)調(diào)用父類(lèi) coordinate_init函數(shù),對(duì)父類(lèi)的各種屬性進(jìn)行初始化,并同時(shí)對(duì)自身的屬性 width 和 height 進(jìn)行初始化,最后返回創(chuàng)建成功后的對(duì)象指針。

rectangle_destroy 用于父類(lèi)對(duì)象屬性的解除初始化,并為對(duì)象屬性重新分配默認(rèn)值,釋放之前申請(qǐng)的內(nèi)存空間,銷(xiāo)毀 rectangle 對(duì)象。

從頭文件 rectangle.h 和源文件 rectangle.c 可以看出,子類(lèi) rectangle 是基于其父類(lèi) coordinate 進(jìn)行聲明和構(gòu)建的,因?yàn)榫匦蝦ectangle除了 width 和 height 屬性外,還包含了坐標(biāo) x 和 y 屬性。

把父類(lèi)放在結(jié)構(gòu)體成員的第一個(gè)位置,是由于結(jié)構(gòu)體內(nèi)存的連續(xù)性,可以很安全地進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換。舉個(gè)例子:假如一個(gè)函數(shù)要求傳入的參數(shù)是 COORDINATE_T 類(lèi)型,但可以通過(guò)強(qiáng)制類(lèi)型轉(zhuǎn)換,傳入 RECTANGLE_T 類(lèi)型的參數(shù),具體的使用方法,可以查看以下測(cè)試函數(shù)。

void rectangle_test_function(void)
{
    P_RECTANGLE_T p_rectangle_1 = NULL;
    P_RECTANGLE_T p_rectangle_2 = NULL;

    //創(chuàng)建兩個(gè) P_RECTANGLE_T 類(lèi)型的類(lèi)對(duì)象
    p_rectangle_1 = (P_RECTANGLE_T)rectangle_create(0,0,150,150);
    p_rectangle_2 = (P_RECTANGLE_T)rectangle_create(200,200,500,500);

    if((NULL != p_rectangle_1) && (NULL != p_rectangle_2)){

        //打印出類(lèi)對(duì)象的初始化屬性,通過(guò)函數(shù)指針的方式來(lái)調(diào)用屬性操作函數(shù)
        printf("p_rectangle_1,x = %d,y = %d,width = %d,height = %d n",
               p_rectangle_1->coordinate.get_x(&(p_rectangle_1->coordinate)), 
               p_rectangle_1->coordinate.get_y(&(p_rectangle_1->coordinate)), 
               p_rectangle_1->width,p_rectangle_1->height);

        printf("p_rectangle_2,x = %d,y = %d,width = %d,height = %d n",    
               p_rectangle_2->coordinate.get_x(&(p_rectangle_2->coordinate)),   
               p_rectangle_2->coordinate.get_y(&(p_rectangle_2->coordinate)),   
               p_rectangle_2->width,p_rectangle_2->height);

        //修改類(lèi)對(duì)象的屬性,注意這里有兩種方式,1、通過(guò)強(qiáng)制類(lèi)型轉(zhuǎn)換修改。2、通過(guò)正常方式修改
        p_rectangle_1->coordinate.moveby((P_COORDINATE_T)p_rectangle_1, 50, 50);
        p_rectangle_2->coordinate.moveby(&(p_rectangle_2->coordinate), 50, 50);

        //再次打印出類(lèi)對(duì)象的修改后的屬性
        printf("after moveby, p_rectangle_1,x = %d,y = %d,width = %d,height = %d n",
               p_rectangle_1->coordinate.get_x(&(p_rectangle_1->coordinate)), 
               p_rectangle_1->coordinate.get_y(&(p_rectangle_1->coordinate)), 
               p_rectangle_1->width,p_rectangle_1->height);

        printf("after moveby, p_rectangle_2,x = %d,y = %d,width = %d,height = %d n",    
               p_rectangle_2->coordinate.get_x(&(p_rectangle_2->coordinate)),   
               p_rectangle_2->coordinate.get_y(&(p_rectangle_2->coordinate)),   
               p_rectangle_2->width,p_rectangle_2->height);
    }

    //銷(xiāo)毀類(lèi)對(duì)象
    rectangle_destroy(p_rectangle_1);
    rectangle_destroy(p_rectangle_2);
}

測(cè)試函數(shù)的運(yùn)行效果,如下圖所示:

p_rectangle_1,x = 0,y = 0,width = 150,height = 150
p_rectangle_2,x = 200,y = 200,width = 500,height = 500
after moveby, p_rectangle_1,x = 50,y = 50,width = 150,height = 150
after moveby, p_rectangle_2,x = 250,y = 250,width = 500,height = 500

通過(guò)上述代碼的測(cè)試,可以總結(jié)出以下幾點(diǎn)內(nèi)容:

1、外部函數(shù)可以通過(guò)子類(lèi)直接使用父類(lèi)的各個(gè)成員,但只能通過(guò)子類(lèi)結(jié)構(gòu)體的第一個(gè)成員來(lái)訪問(wèn)。

2、父類(lèi)放在子類(lèi)結(jié)構(gòu)體的第一個(gè)位置,由于結(jié)構(gòu)體內(nèi)存的連續(xù)性,因此可以通過(guò)強(qiáng)制類(lèi)型轉(zhuǎn)換來(lái)直接訪問(wèn)。

3、由于C語(yǔ)言結(jié)構(gòu)體的特性,即使子類(lèi)存在與父類(lèi)同名的函數(shù),父類(lèi)的函數(shù)不會(huì)被子類(lèi)的函數(shù)覆蓋和重寫(xiě),因此,子類(lèi)與父類(lèi)之間不存在函數(shù)重載。

源碼下載地址:https://github.com/embediot/my_program_test

感謝閱讀!

推薦器件

更多器件
器件型號(hào) 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊(cè) ECAD模型 風(fēng)險(xiǎn)等級(jí) 參考價(jià)格 更多信息
74LVC14AD,118 1 Nexperia 74LVC14A - Hex inverting Schmitt trigger with 5 V tolerant input@en-us SOIC 14-Pin

ECAD模型

下載ECAD模型
$0.38 查看
ECS-.327-12.5-39-TR 1 ECS International Inc Parallel - Fundamental Quartz Crystal, 0.032768MHz Nom, ROHS COMPLIANT, MINIATURE, CERAMIC, SMD, 2 PIN

ECAD模型

下載ECAD模型
$1.31 查看
510BBA125M000BAG 1 Silicon Laboratories Inc LVDS Output Clock Oscillator, 125MHz Nom, ROHS COMPLIANT PACKAGE-6

ECAD模型

下載ECAD模型
$4.22 查看

相關(guān)推薦