C中的派生类-您最喜欢的方法是什么?
根据我在面向对象C编程方面的经验,我看到了两种实现派生类的方法C中的派生类-您最喜欢的方法是什么?,c,oop,C,Oop,根据我在面向对象C编程方面的经验,我看到了两种实现派生类的方法 第一个方法将父类定义为.h文件。然后,从该类派生的每个类都将有do: 文件parent_class.h: int member1; int member2; 文件测试.c: struct parent_class { #include "parent_class.h" // must be first in the struct } struct my_derived_class { #include "par
第一个方法将父类定义为.h文件。然后,从该类派生的每个类都将有do: 文件parent_class.h:
int member1;
int member2;
文件测试.c:
struct parent_class {
#include "parent_class.h" // must be first in the struct
}
struct my_derived_class {
#include "parent_class.h" // must be first in the struct
int member3;
int member4;
}
struct parent_class {
int member3;
int member4;
}
struct my_derived_class {
struct parent_class; // must be first in the struct
int member3;
int member4;
}
第二种方法可以: 文件测试.c:
struct parent_class {
#include "parent_class.h" // must be first in the struct
}
struct my_derived_class {
#include "parent_class.h" // must be first in the struct
int member3;
int member4;
}
struct parent_class {
int member3;
int member4;
}
struct my_derived_class {
struct parent_class; // must be first in the struct
int member3;
int member4;
}
你最喜欢用C编写派生类的方法是什么?为什么
您喜欢哪种方法,第一种方法还是第二种方法(或您自己的方法)?我使用的代码使用了第一种方法 我能想到的使用第一种方法的唯一两个原因是:
如果你能用方法2做同样的事情,那么我认为这两种方法是相等的。我知道GNOME使用第二种方法,而且投射指针也是已知的事情。我不记得有什么真正的障碍可以通过这样做。事实上,从C内存模型的角度来看,两者之间不可能有任何语义上的差异,因为AFAIK唯一可能的差异是编译器在结构填充上的差异,但由于代码都在同一个编译器中运行,这将是一个没有实际意义的问题。我以前使用过方法#2,发现它工作得很好:
- 如果基类型是派生类型中的第一个成员,则可以随时向上转换到该基类型
- 与其一直取消引用以获取基成员,只需保留两个指针:一个用于基接口,一个用于派生接口
free()
当然也会释放派生字段,所以这不是问题
此外,我发现在多态情况下访问基字段是我倾向于做的事情:我只关心那些关心基类型的方法中的字段。派生类型中的字段用于只对派生类型感兴趣的方法。我是使用方法2的库的维护者之一。与方法1一样有效,但没有任何预处理器欺骗。或者它实际上工作得更好,因为您可以使用以基类为参数的函数,并且您可以强制转换到基类结构,C保证这对第一个成员有效 更有趣的问题是,如何实现虚拟函数?在我们的例子中,结构有指向所有函数的指针,初始化设置了它们。它稍微简单一些,但比使用指向共享vtable的指针的“正确方式”有更多的空间开销
<>无论如何,我宁愿用C++而不是用C语言来压缩它,但是政治……/P> < P>第二种方法具有继承方法的类型安全的优点。如果您想要有一个方法foo(struct parent_class bar)并用foo((struct parentclass)派生的_class)调用它,这将正常工作。C标准对此进行了定义。因此,我通常更喜欢方法2。通常,可以保证,将结构强制转换到其第一个成员将生成包含该结构的第一个成员的数据的结构,而不管内存的布局如何 在以前的工作中,我们使用了一个预处理器来处理这个问题。我们使用简单的C++风格语法声明类,预处理器生成的C头与第一个方法基本相同,但没有#includes。它还做了一些很酷的事情,比如生成vtable和宏以进行上下转换
<>请注意,这是在我们所有目标平台都存在良好C++编译器之前的日子。现在,这样做是愚蠢的。第一种方法很可怕,它隐藏了重要信息。我从不使用它,也不允许它被使用。即使使用宏也会更好:
#define BODY int member1; \
int member2;
struct base_class
{
BODY
};
但由于其他人指出的原因,方法2要好得多
我更喜欢第一种方法,因为您可以将派生类指针强制转换为父类,而无需担心
相反
C标准保证结构的地址是第一个成员的地址,因此在第二种情况下,可以安全地将指向派生的指针强制转换为父级,因为派生的第一个成员是父级结构,如果不是成员,则将结构作为成员转换为与同一结构相同的布局,因此,将指针强制转换为派生到父对象将始终有效
第二种情况并非如此。两个结构的某些成员定义为同一类型,这些成员之间的填充可能不同
64位bigendian编译器编译是合理的
struct A { a uint64_t; b uint32_t; };
这样sizeof(A)是8的整数倍,b是64位对齐的,但是编译
struct B { a uint64_t; b uint32_t; c uint32_t; };
因此,sizeof(B)是8的整数倍,但B只有32位对齐,因此不会浪费空间。第二个选项强制您编写非常长的名称,如
myobj.parent.祖父母.attribute
,这很难看。从语法的角度来看,第一个选项更好,但将子对象强制转换为父对象有点冒险——我不确定标准是否保证不同的结构对相似的成员具有相同的偏移量。我猜编译器可能会对这样的结构使用不同的填充
如果您使用的是GCC-anonymous struct members,这是MS扩展的一部分,那么还有另一种选择,因此我猜它是由某些MS编译器发起的,并且仍然可能受到MS的支持
声明看起来像
struct shape {
double (*area)(struct shape *);
const char *name;
};
struct square {
struct shape; // anonymous member - MS extension
double side;
};
struct circle {
struct shape; // anonymous member - MS extension
double radius;
};
在“构造函数”函数中,您需要