我是初学者,有很多OOP经验(C#),我无法理解如何在C中实现“多态”的概念。
现在,我正在考虑如何使用结构捕获文件系统的逻辑结构。我有一个包含文件夹和文件的文件夹。此文件夹中的文件夹可以包含其他文件和文件夹等。
我的方法:
typedef enum { file, folder } node_type;
struct node;
typedef struct {
node_type type;
char *name;
struct node *next;
struct node *children;
} node;
这是我能做的最好的吗?我发现了很多关于“C中的多态性”的帖子,但是我想看看这样的多态数据结构如何能够干净有效地构建(就这些结构中未使用的成员浪费的内存而言)。
感谢。
答案 0 :(得分:2)
我希望我理解你想要的东西 - 我不确定,但我想你想做那样的事情:
typedef struct
{
int type; // file or folder?
} Item;
typedef struct
{
struct A;
// data related to a file
} File;
typedef struct
{
struct A;
// data related to a folder - like pointer to list of Item
} Folder;
只要两个结构都遵循相同的内存映射(相同的变量)并将其作为子项添加,您就可以在两个结构中正确使用指针。
同时检查一下:How can I simulate OO-style polymorphism in C?
编辑:我不确定上面的语法(从上面的链接中获取)。我习惯这样写它:
typedef struct
{
int type;
// data for file
} File;
typedef struct
{
int type;
// data for folder - list, etc
} Folder;
答案 1 :(得分:2)
C没有内在的多态性概念。
您最终将从头开始实现您想要的机制。这不是一件坏事。它为您提供了更多的灵活性。例如,C ++虚方法每个类都是硬连线的,你不能每个实例改变方法指针。
以下是一些想法:
您的node_type字段提供了一种执行运行时类型查询的方法。更进一步,您可以使用区分(或标记)联合将多个类型打包到一个结构中:http://en.wikipedia.org/wiki/Tagged_union。我不确定变体类型是否有资格作为OO。
多态性通常与行为有关。您可以在结构中存储function pointers(“方法”),并指向不同的函数,为不同的对象实例提供不同的行为。 C ++的处理方式是每个类都有一个函数指针表,然后每个对象实例引用它的类的表(顺便提一下,表指针也可以扮演你的node_type for RTTI的角色)。这称为virtual method table。
数据继承意味着子类包含所有基类的数据成员以及一些额外的东西。在C中,最简单的方法是将基类结构嵌入派生类结构的头部。这样一个指向derived的指针是指向base的指针。
typedef struct BaseClass {
int baseMember;
} BaseClass;
typedef struct DerivedClass {
BaseClass base;
int derivedMember;
} DerivedClass;
你可能比阅读Stanley B. Lippman撰写的“Inside the C ++ Object Model”更糟糕。例如,如果您想了解如何实现多重继承,这将有所帮助。
答案 2 :(得分:1)
以下是基于X / Motif古代记忆的老派C多态性的例证。
如果你只想要一个有区别的联合(或者只是一个带有子指针的类型结构可能为null),那么在你的情况下它可能更简单。
enum NodeType { TFile, TFolder };
struct Node {
enum NodeType type;
const char *name;
struct Node *next;
};
struct FileNode {
struct Node base_;
};
struct FolderNode {
struct Node base_;
struct Node *children;
/* assuming children are linked with their next pointers ... */
};
以下是构造函数 - 我将把链表填充为读者的练习......
struct Node* create_file(const char *name) {
struct FileNode *file = malloc(sizeof(*file));
file->base_.type = TFile;
file->base_.name = name; /* strdup? */
file->base_.next = NULL;
return &file->base_;
}
struct Node* create_folder(const char *name) {
struct FolderNode *folder = malloc(sizeof(*folder));
folder->base_.type = TFolder;
folder->base_.name = name;
folder->base_.next = NULL;
folder->children = NULL;
return &folder->base_;
}
现在我们可以走一个层次结构,检查每个节点的类型并做出适当的响应。这依赖于第一个成员子对象具有零偏移到父对象 - 如果不成立(或者您需要多继承),则必须使用offsetof
在基本类型和“派生”类型之间进行转换。
void walk(struct Node *root,
void (*on_file)(struct FileNode *),
void (*on_folder)(struct FolderNode *))
{
struct Node *cur = root;
struct FileNode *file;
struct FolderNode *folder;
for (; cur != NULL; cur = cur->next) {
switch (cur->type) {
case TFile:
file = (struct FileNode *)cur;
on_file(file);
break;
case TFolder:
folder = (struct FolderNode *)cur;
on_folder(folder);
walk(folder->children, on_file, on_folder);
break;
}
}
}
请注意,我们有一种多态基类型,但是我们可以使用虚函数进行更完整的多态设置,而不是打开类型枚举。只需添加一个指向Node
的函数指针,如:
void (*visit)(struct Node *self,
void (*on_file)(struct FileNode *),
void (*on_folder)(struct FolderNode *));
并让create_file
和create_folder
将其设置为适当的功能(例如visit_file
或visit_folder
)。然后,walk
只会调用
cur->visit(cur, on_file, on_folder);