代码之家  ›  专栏  ›  技术社区  ›  Mason Wheeler

如何在非OO语言中进行继承?

  •  2
  • Mason Wheeler  · 技术社区  · 15 年前

    我读到早期C++编译器实际上把C++代码翻译成C,在后端使用了C编译器,这让我感到惊奇。我已经掌握了足够的技术知识,可以将大部分工作方法都考虑在内,但是如果没有语言支持,我就无法弄清楚如何进行类继承。

    具体来说,如何定义一个具有几个字段的类,然后继承它的一系列子类,每个子类都添加自己的新字段,并能够将它们作为函数参数交替传递?特别是当C++允许您在堆栈上分配对象时,如何做到这一点,因此您甚至可能没有指针隐藏在后面?

    注意:我得到的最初几个答案是关于多态性的。我对多态性和虚拟方法了如指掌。我甚至做过一次会议演示,讨论Delphi中虚拟方法表如何工作的低级细节。我想知道的是类继承和字段,而不是多态性。

    7 回复  |  直到 15 年前
        1
  •  5
  •   Michael Burr    15 年前

    无论如何,在C++中,当c++代码被翻译成C时,cfAdvor使用它来做它,但是你需要很有纪律性并且手动完成所有的繁琐工作。

    必须使用执行构造函数工作的函数初始化“类”。这包括为虚拟函数初始化指向多态函数指针表的指针。虚拟函数调用必须通过vtbl函数指针进行(它将指向函数指针的结构-每个虚拟函数一个)。

    每个派生cals的虚函数结构需要是基类的一个超级集合。

    这方面的一些机制可能是使用宏隐藏/辅助的。

    Miro Samek的第一版 "Practical Statecharts in C/C++" 有一个附录A——“C+面向对象的C语言编程”,它有这样的宏。看起来这是从第二版中删除的。可能是因为麻烦太多了。只要使用C++,如果你想这样做…

    你也应该阅读 Lippman's "Inside the C++ Object Model" 这涉及到C++如何在幕后工作,这通常是关于如何在C.工作的小片段。


    我想我知道你在找什么了。也许吧。

    像这样的东西如何工作:

    typedef 
    struct foo {
        int a;
    } foo;
    
    void doSomething( foo f);   // note: f is passed by value
    
    typedef
    struct bar {
        foo base;
        int b;
    } bar;
    
    
    int main() {
        bar b = { { 1 }, 2};
    
        doSomething( b);    // how can the compiler know to 'slice' b
                            //  down to a foo?
    
        return 0;
    }
    

    如果没有语言支持,你就不能这么简单地做到这一点——你需要手动做一些事情(这就是没有语言支持的意思):

        doSomething( b.base);       // this works
    
        2
  •  5
  •   Artelius    15 年前

    基本上,结构中的结构。

    struct Base {
        int blah;
    };
    
    struct Derived {
        struct Base __base;
        int foo;
    };
    

    当你想的时候,比如说 Derived * Base * ,您实际上会返回一个指向 __base 派生结构的元素,在本例中,它是结构中的第一个元素,因此指针应该是相同的(但对于多个继承类则不是这样)。

    如果您想访问 blah 在这种情况下,你会做一些像 derived.__base.blah .

    虚函数通常使用一个特殊的函数指针表来完成,该表是每个对象的一部分,是一种基本类型的“我的类型”记录。

        3
  •  2
  •   Andrew Keith    15 年前

    下面是COM如何处理C语言。我有点生疏,但本质就是这样。每个“类”成员变量只是一个结构。

    struct Shape
    {
      int value;
    };
    
    struct Square
    {
      struct Shape shape; // make sure this is on top, if not KABOOM
      int someothervalue;
    };
    

    所有的方法,实际上只是普通的函数。这样地

    void Draw(Shape * shape,int x,int y)
    {
      shape->value=10; // this should work even if you put in a square. i think...
    }
    

    然后,他们使用预处理器“欺骗”C代码以显示类似的内容。

    Square * square;
    square->Draw(0,0); // this doesnt make sense, the preprocessor changes it to Draw(square,0,0);
    

    唉,我不知道用什么样的预处理器技巧来让C++看起来函数调用分解成一个计划香草C调用。

    DirectX COM对象以这种方式声明。

        4
  •  1
  •   Boojum    15 年前

    Dobb博士就这个问题发表了一篇相当详细的文章, Single Inheritance Classes in C.

        5
  •  1
  •   Edmund    15 年前

    结构中的结构是常见的,但它使访问继承字段变得困难。您要么需要使用间接(例如 child->parent.field (或)铸造 ((PARENT *) child)->field )

    我看到的另一种选择更像这样:

    #define COUNTRY_FIELDS \
        char *name; \
        int population;
    
    typedef struct COUNTRY
    {
        COUNTRY_FIELDS
    } COUNTRY;
    
    #define PRINCIPALITY_FIELDS \
        COUNTRY_FIELDS \
        char *prince;
    
    typedef struct PRINCIPALITY
    {
        PRINCIPALITY_FIELDS
    } PRINCIPALITY;
    

    这使类型可以直接访问继承字段。结果对象仍然可以安全地强制转换为父类型,因为父类型的字段和继承的字段在同一位置开始。

    宏可以稍微改进语法。我在旧的POV瑞源中看到了这一点(但我认为它们已经转换成C++)了。

        6
  •  0
  •   ssteidl    15 年前

    如果你想要一个关于这些东西如何工作的好的参考资料,请看一下glib/gdk/gtk开放源码库。它们有相当好的文档,并且整个框架是基于C OO的。

        7
  •  0
  •   David Harris    15 年前

    您可以通过编写构造函数、setter、getter和析构函数来模拟对象,并显式调用隐藏的该指针。

    继承是通过让派生对象包含指向派生对象结构中基对象的指针来处理的。