程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C 語言實現多態的原理:函數指針

C 語言實現多態的原理:函數指針

編輯:關於C語言

C語言實現多態的原理:函數指針

何為函數指針?答案:C Programming Language. 可以查閱下,從原理上來講,就是一個內存地址,跳過去執行對應的代碼段。 既然如此,在運行時決定跳到哪個地方去執行特定的代碼即可。

一個簡單的版本:

以音頻解碼器作為例子:AAC 解碼器,Mpeg解碼器,以及其他類型的解碼器。 那手動的多態可能會這樣實現:
U32 audioHandle = AudioDecOpen(int type)
{
    if(type == aac)
	return aac_open();
    else if(type == mpeg)
        return mpeg_open();
}


這樣的代碼不利於擴展,沒加入一個新的實例,就得改動AudioDecOpen這個函數。而且封裝的不好。

另外一種方法來寫:

首先定義三種公有函數的函數指針。
typedef int (*OpenFunc) (void *this);
typedef int (*CloseFunc) (void *this);
typedef int (*ControlFunc) (void *this, int command, void *param);

定義公共接口結構體 & AudioDecoder 對象:
struct module
{ OpenFunc Open; CloseFunc Close; ControlFunc Control;};
struct AudioDecoder{
    struct module m;
    int audioType;
    void* private;
};


提供一個表驅動來方便找到對應的入口:
struct AudioPool{
    int audioType;
    struct module* audioModule;
}pool[] = {
     {aac , aac_module},
     {mpeg , mpeg_module},
};

int AudioCreate(int type , Handle *handle)
{
    AudioDecoder dec = alloc_audioDec();
    
    foreach(pool , k)
    {
          if(k->audioType == type)
          {
                 dec->m = k->audioModule;
          }
    }
    *handle = (Handle)dec;
}
這樣,當外界去Create一個Audio的對象時,就已經初始化好對應的函數入口了。Open就非常簡單了:
int AudioOpen(struct AudioDecoder *dec)
{
     return dec->m->Open(dec);
}
其中AudioDecoder中的Private 則是在各自的Open中自己申請,自己釋放,Close,Control 類似。
今後維護這個表驅動即可(pool),新的對象的支持加入進來就行了,很方便維護。

更好的維護pool

現在的pool依然拓展性不太好,畢竟每次加入新的對象都得改動pool這個表驅動。 這裡提供一個更好的方法:
struct AudioPool{
    int audioType;
    struct module* audioModule;
}pool[MAX_POOL];

在提供一個Pool_Register(int type , struct module* module); 的功能:
int Pool_Register(int type , struct module* module); 
{
    for_each(pool , k)
    {
          if(k->type == INVALID_AUDIO_TYPE)
          {
                 k->type = type;
                 k->audioModule = module;
           }
    }

    if(k == NULL)
    {
        return REACH_POOL_END;
     }
    return NO_ERROR;
}

這樣在每個實例中調用 rigister 就可以很優雅的解決這個問題。
附上兩個解碼器的對象的代碼,Mpeg的解碼器使用的是 libmad , aac的解碼器使用的是 libfaad 庫: AAC代碼片段: \
.
.
.
static int Close(void *this)
{
	AudioSoftDecoder *ad = (AudioSoftDecoder*)this;
	if(!ad || !ad->privateData)
	{
		syslog(LOG_ERR , "%s(%d):Bad Parameter  !!!\n"  , __FUNCTION__ , __LINE__ );
		return CT_ERROR_BAD_PARAMETER;
	}
	AacFaadPrivate *private = (AacFaadPrivate *)ad->privateData;
	
	private->exit = TRUE;

	if(private->decoderPid > 0)
	{
		pthread_join(private->decoderPid , NULL);	
	}

	if(private->hDecoder)
	{
		NeAACDecClose(private->hDecoder);
	}

	free(private);

	ad->privateData = NULL;
	return CT_ERROR_NO_ERROR;
}

int AAC_Init()
{
	return RegisterAudioSoftDec(AudioDecType_AAC ,&aacModule);
}


MPEG代碼片段:
.
.
.
int Close(void *this)
{
	AudioSoftDecoder *ad = (AudioSoftDecoder*)this;
	if(!ad || !ad->privateData)
	{
		syslog(LOG_ERR , "%s(%d):Bad Parameter  !!!\n"  , __FUNCTION__ , __LINE__ );
		return CT_ERROR_BAD_PARAMETER;
	}
	mpegMadPrivate *private = (mpegMadPrivate *)ad->privateData;
	
	private->exit = TRUE;

	if(private->decoderPid > 0)
	{
		pthread_join(private->decoderPid , NULL);	
	}

	mad_decoder_finish(&private->decoder);

	if(private->data.buffer)
	{
		free(private->data.buffer);
	}

	free(private);
	ad->privateData = NULL;
	return CT_ERROR_NO_ERROR;
}

int Control(void *this , U32 cmd ,void* param)
{
	return CT_ERROR_NO_ERROR;
}

int MPEG_Init()
{
	return RegisterAudioSoftDec(AudioDecType_MPEG ,&mpegModule);
}



總結:

使用面向對象來設計自己的代碼,維護上能夠減少很多工作量。在C語言裡面還實現了MVC模式等,這部分也是函數指針實現的,實際上只是一個回調。但是代碼維護,模塊劃分上,非常清晰。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved