你如何用非OO语言进行继承?

时间:2009-10-29 04:26:27

标签: c++ c oop inheritance history

我读到早期的C ++“编译器”实际上将C ++代码翻译成C并在后端使用了C编译器,这让我很奇怪。我已经掌握了足够的技术知识来解决大部分工作原理,但是如果没有语言支持,我无法弄清楚如何进行类继承。

具体来说,如何定义一个包含几个字段的类,然后是一堆继承自它的子类,每个子类都添加自己的新字段,并能够作为函数参数交替传递它们?特别是当C ++允许你在堆栈上分配对象时你怎么能这样做呢?所以你甚至可能没有隐藏指针的指针?

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

7 个答案:

答案 0 :(得分:5)

无论如何,在C语言中,当C ++代码被翻译成C语言时,你就像C ++在C ++早期那样做。但是你需要非常自律,并且手动完成所有繁琐的工作。

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

每个派生calss的虚函数结构需要是基类的超集。

使用宏可以隐藏/辅助某些机制。

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

答案 1 :(得分:5)

基本上,结构在结构内。

struct Base {
    int blah;
};

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

当您想要将Derived *强制转换为Base *时,您实际上会返回一个指向Derived结构的__base元素的指针,在这种情况下是结构中的第一件事,所以指针应该是相同的(虽然不是多继承类的情况)。

如果您想在这种情况下访问blah,您可以执行derived.__base.blah之类的操作。

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

答案 2 :(得分:2)

这是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 ++看起来函数调用解析为计划vanilla C调用。

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

答案 3 :(得分:1)

博士。 Dobb有一篇关于这个主题的中等详细文章,Single Inheritance Classes in C.

答案 4 :(得分:1)

结构内的结构很常见,但是访问继承的字段会很麻烦。您需要使用间接(例如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-Ray源中看到了这一点(但我认为它们已经转换为C ++)。

答案 5 :(得分:0)

如果您想要了解这些内容的工作原理,请查看glib / gdk / gtk开源库。他们有很好的文档,整个框架都基于C OO。

答案 6 :(得分:0)

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

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