2009-10-15 41 views
1

我想在C.探索OOP不過,我一個C的n00b,並想挑計算器:)輝煌的大腦OOP在C,實施和錯誤

我的代碼如下:

#include <stdio.h> 
#include <stdlib.h> 

typedef struct speaker { 
    void (*say)(char *msg); 
} speaker; 

void say(char *dest) { 
    printf("%s",dest); 
} 

speaker* NewSpeaker() { 
    speaker *s; 
    s->say = say; 
    return s; 
} 

int main() { 
    speaker *s = NewSpeaker(); 
    s->say("works"); 
} 

但是,我得到一個段錯誤,如果我然而刪除所有參數,並使其無效,我可以讓它正常工作。我現在的代碼有什麼問題?

另外。雖然這在C中實現了一種對象形式,但我試圖通過繼承來進一步實現它,甚至覆蓋/重載方法。你如何看待我可以實現這樣的?

謝謝!

+0

這些都是3個很好的答案!我選擇了最高的答案,並且我給了你們所有人的讚揚,謝謝!我從來沒有想過自己。 – nubela

回答

15

在您的代碼中,NewSpeaker()實際上並未創建「新」揚聲器。您需要使用內存分配功能,例如malloccalloc

speaker* NewSpeaker() { 
    speaker *s = malloc(sizeof(speaker)); 
    s->say = say; 
    return s; 
} 

在不偏離,例如,malloc返回值,s初始化爲垃圾在堆棧中,因此段錯誤分配值。

+2

元評論:通過「初始化爲堆棧上的垃圾」,我的意思是發生在那裏的任何事情。事實上,沒有任何東西將「s」設置爲主動意義上的值。被動地獲取內存中發生的任何事情的內容。 – user7116

+2

好的答案,但它通常是不好的做法,從malloc()返回的值,至少在C ... –

+0

良好的捕獲。一些我堅持的編譯器需要它,刪除。 – user7116

2

您可以通過將父類結構嵌入到子類結構的頂部來實現繼承。這樣你可以安全地從子類轉換到父類。這裏的an article在C中實現面向對象的功能。如果你想要一個現有的解決方案,或者只是想了解更多關於實現面向對象的方法,請看GObject library

+0

有趣,這實際上是最好的答案...... :) – falstro

3

首先,正如已經注意到的那樣,您未能爲'NewSpeaker'中的'speaker'對象分配內存。沒有無用的垃圾,它看起來如下

speaker* NewSpeaker(void) 
{ 
    speaker *s = malloc(sizeof *s); 
    s->say = say; 
    return s; 
} 

注意,有對malloc的結果沒有投中,「的sizeof」的說法沒有類型名稱,函數參數列表聲明爲「(無效) ', 不只是 '()'。

其次,如果你希望能夠建立你的「揚聲器」類型的非動態對象,你可能需要先提供就地初始化函數,然後從那裏

speaker* InitSpeaker(speaker* s) 
{ 
    assert(s != NULL); 
    s->say = say; 
    return s; 
} 

speaker* NewSpeaker(void) 
{ 
    void *raw = malloc(sizeof(speaker)); 
    return raw != NULL ? InitSpeaker(raw) : NULL; 
} 

繼續最後,如果你真的想創建類似於虛擬C++方法的東西,你需要爲每個方法提供一個'this'參數(以訪問你的對象的其他成員)。因此,它或許應該看起來像

typedef struct speaker 
{  
    void (*say)(struct speaker *this, char *msg); 
} speaker; 

void say(speaker *this, char *dest) 
{ 
    printf("%s",dest); 
} 

這,當然,會要求你每次所謂的「方法」時間通過相應的參數,但有沒有辦法解決這個。

此外,我希望你知道你只需要「方法」指針在你的「類」爲「虛擬方法」。普通(非虛擬)方法不需要這樣的指針。最後,「傳統的」C++類實現不在類的每個實例中存儲虛擬方法指針。相反,它們被放置在一個單獨的表(VMT)中,指向哪個指針被添加到每個實例。這節省了大量的內存。順便說一句,當你實現繼承時,BTW會特別有用。