0

私は C の初心者であり、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 のポリモーフィズム」に関する多くの投稿を見つけましたが、このようなポリモーフィック データ構造をクリーンかつ効率的に構築する方法を確認したいと思います (これらの構造の未使用メンバーで浪費されるメモリの観点から)。

ありがとう。

4

3 に答える 3

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;

両方の構造体が同じメモリ マッピング (同じ変数) に従い、子として追加される限り、両方の構造体でポインターを適切に使用できます。

これもチェックしてください: C でオブジェクト指向スタイルのポリモーフィズムをシミュレートするにはどうすればよいですか?

編集:上記の構文についてはわかりません(上記のリンクから取得しました)。私は代わりにこのように書くことに慣れています:

typedef struct
{
  int type;
  // data for file
} File;

typedef struct
{
  int type;
  // data for folder - list, etc
} Folder;
于 2013-08-09T15:46:28.743 に答える
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_filecreate_folder設定します(たとえば、visit_fileまたはvisit_folder)。次に、列挙型をオンにする代わりに、walk単に呼び出します

cur->visit(cur, on_file, on_folder);
于 2013-08-09T16:48:27.013 に答える