C中的继承:好,坏还是其他?

时间:2012-01-19 15:53:20

标签: c oop

我们拥有一个已经开发了几十年的大型C代码库。代码的一个特性是对函数指针和伪继承的大量依赖。成语(如所讨论的here)如下:

typedef struct twod_ {
    double x, y;
} twod;

typedef struct threed_ {
    twod super;
    double z;
} threed;

threed *point_3d;
twod *point_2d = (twod *)point3d;

此时point_2d->xpoint_3d->x是同一块内存。

我的问题是:

  • 这个成语在现代生产代码中仍然很受欢迎吗? (任何推荐的开源示例)
  • 此代码需要性能 - 这个成语是否有助于速度和/或内存使用?
  • 它实现的方式(或由于多年的代码膨胀)现在感觉有点像意大利面条代码 - 一般来说,这是实现的问题还是成语?换句话说,在一个理想的世界里,用这个成语来500k LOC会很快被理解吗?

当然,格言“如果它没有破坏,不要修复它”将是一个很好的记住;但是,目前这并没有真正帮助我们,所以我们认为我们可能需要更深入地进行重构......

谢谢!

4 个答案:

答案 0 :(得分:2)

我会说这很好,虽然当然它很容易混淆,因为它变得相当冗长。

对我来说,“C with classes”的旗舰开源实现可能是GTK+,特别是gobject模块。

答案 1 :(得分:1)

这个工作,但是你必须记住的问题是编译器不会就变量访问或类似的问题向你发出警告/说什么。这种方法不是类型安全的,因为有时这种显式转换可能是邪恶的。以这个例子为例:

typedef struct _A
{
    double x, y;
} A;

typedef struct _B 
{
    A super;
    double z;
} B;

typedef struct _C 
{
    A super; // we wanted B, but wrote A by mistake
    double w;
} C;

C* c;
B* b = (B*)c;

// will write to C::w instead of B::z and no one will warn you about this.
// you'll need to track this by hand after your application crashes.
b->z = 1234; 

这种类型的东西可以轻松地将飞机放在地面上。 :)

关于性能,这将最终做同样的事情,就像你手动指针超级(比铸造更安全):

A* a;
B* b = &a->b; 

此外我认为这种情况并不受欢迎(至少对于像我这样的年轻程序员来说,在更现代的编译器中增加了编程)

另外,C ++编译器是一个选项吗?现在更多的现代编译器允许结构继承 - 比这种方式更好。

答案 2 :(得分:0)

喜欢(差不多)C中的所有内容,只要你知道你做了什么,就没问题了。 但请注意,问题可能非常容易出现 - 例如,如果您有一个threed数组,则无法将其强制转换为twod数组。 (访问该铸造数组的成员是未定义的行为)

答案 3 :(得分:0)

  

这个成语在现代生产代码中仍然很受欢迎吗? (任何   推荐的开源示例)

虽然你时不时地看到它,但这种情况有点罕见。我不会称之为流行,C程序中多态性的使用往往很少。

  

此代码需要性能 - 这个成语是否有助于速度和/或内存使用?

它肯定不会使代码更多比包含所有3个成员的结构更有效。在最坏的情况下,遗产将使代码效率降低。最好的方法是反汇编到特定平台的编译器输出,并亲自看看。

  

......这是实施问题还是成语?

继承一般应该谨慎使用,继承为继承填充没有目的。所有OO语言都是如此。

就我个人而言,我确实认为这种特殊的习惯用法和类似尝试在C中包含温和有用的OO特征是有问题的。主要是因为它们增加了程序的复杂性。像“使用ANSI-C进行面向对象编程”(Axel-Tobias Schreiner,90年代早期发布)一书中提到了这样的事情。我个人认为那本书中的大部分内容都非常糟糕。

  

在一个理想的世界里,用这个成语很快就能达到500k LOC   理解?

这取决于程序结构,文件结构和编码标准,而不是这个小OO习语。虽然程序是最先进的,但当然不会很快理解500k loc。