2013-09-25 40 views
4

我處於一個我需要獲得一些在C中工作的面向對象特性的位置,尤其是繼承。幸運的是,在堆棧溢出方面有一些很好的參考,特別是這個Semi-inheritance in C: How does this snippet work?和這個Object-orientation in C。該想法是包含基類的一個實例,在派生類中,並強制轉換它,像這樣:C類繼承通過類型打點,不包含?

struct base { 
    int x; 
    int y; 
}; 

struct derived { 
    struct base super; 
    int z; 
}; 

struct derived d; 
d.super.x = 1; 
d.super.y = 2; 
d.z = 3; 
struct base b = (struct base *)&d; 

這是偉大的,但它變得深繼承樹繁瑣 - 我將有大約鏈5-6「類」,我真的不想一直鍵入derived.super.super.super.super.super.super.super。我希望的是,我可以強制轉換爲第n個元素的結構,像這樣:

struct base { 
    int x; 
    int y; 
}; 

struct derived { 
    int x; 
    int y; 
    int z; 
}; 

struct derived d; 
d.x = 1; 
d.y = 2; 
d.z = 3; 
struct base b = (struct base *)&d; 

我上附帶的Visual Studio 2012和它的作品的C編譯器進行測試,但我有不知道C標準實際上是否能夠保證它。有沒有人可以肯定地知道這是否正確?我不想寫一堆代碼,只是發現它在這樣一個基本的層面上被打破了。

+1

我想如果你注意了編譯器的struct packing規則(想想父類的邊界),你可以使它工作。但它看起來很脆弱,不夠優雅或便攜。並不容易維護。 –

+0

看起來像這個問題的接受答案http://stackoverflow.com/a/3766251/2770712解釋得很好。 – Freddie

+1

@Freddie,這是一個很好的鏈接。但與OP在談論深層次和可能不是統一類型的情況相比,這是一個非常簡單的案例。如果所有的字段都是指針或整數,那麼這不是問題,但只要你進入奇怪的打包和大小的結構,那麼將它們「組合」在一起將取決於編譯器的結構封裝。只是我2美分。 –

回答

-1

struct的地址是其保證的第一個元素的地址。

-1

通過使用指針,您始終可以在層次結構中的任何級別創建對基類的引用。如果您使用某種繼承結構的描述,則可以生成構建步驟所需的「類定義」和工廠函數。

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

struct foo_class { 
    int a; 
    int b; 
}; 

struct bar_class { 
    struct foo_class foo; 
    struct foo_class* base; 

    int c; 
    int d; 
}; 

struct gazonk_class { 
    struct bar_class bar; 
    struct bar_class* base; 

    struct foo_class* Foo; 

    int e; 
    int f; 
}; 

struct gazonk_class* gazonk_factory() { 
    struct gazonk_class* new_instance = malloc(sizeof(struct gazonk_class)); 

    new_instance->bar.base = &new_instance->bar.foo; 
    new_instance->base = &new_instance->bar; 
    new_instance->Foo = &new_instance->bar.foo; 

    return new_instance; 
} 

int main(int argc, char* argv[]) { 
    struct gazonk_class* object = gazonk_factory(); 

    object->Foo->a = 1; 
    object->Foo->b = 2; 

    object->base->c = 3; 
    object->base->d = 4; 

    object->e = 5; 
    object->f = 6; 

    fprintf(stdout, "%d %d %d %d %d %d\n", 
     object->base->base->a, 
     object->base->base->b, 
     object->base->c, 
     object->base->d, 
     object->e, 
     object->f); 

    return 0; 
} 

在這個例子中,你既可以使用base指針上班回來的路上,或直接引用的基類。

3

這裏描述的是一個完全可移植的構造,基本上可以保證通過語言的設計工作,除了標準的作者不認爲有必要明確要求編譯器支持顯然應該起作用的東西。 C89指定工會的公共初始序列規則,而不是指向結構的指針,因爲給定:其中收到struct s1*到外部對象,要麼 一個union u*或malloc分配對象可以合法投

struct s1 {int x; int y; ... other stuff... }; 
struct s2 {int x; int y; ... other stuff... }; 
union u { struct s1 v1; struct s2 v2; }; 

代碼它到union u* 如果該類型是對齊的,並且它可以合法投所得 指針struct s2*,以及使用訪問的效果無論是struct s1*struct s2*將必須是相同經由任一v1或訪問聯合v2成員。因此,編譯器制定所有指示規則的唯一方法就是說,將一種結構類型的指針轉​​換爲另一種類型的指針並使用該指針來檢查公共初始序列的成員將起作用。

不幸的是,編譯器編寫者曾經說過,CIS規則只適用於底層對象有聯合類型的情況,儘管事實上這樣的事情代表了一個非常罕見的用例(與聯合類型存在的情況相比爲了讓編譯器知道指向結構的指針應該爲了檢查CIS而互換處理),並且進一步由於代碼很少接收struct s1*struct s2*來標識union u中的對象,所以他們認爲他們應該被允許忽略這種可能性。因此,即使上面的聲明是可見的,gcc也會假設struct s1*決不會用於從struct s2*訪問CIS的成員。