web-dev-qa-db-ja.com

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

Cプログラミング言語でオブジェクト指向のようなコードを書く方法はありますか?


こちらもご覧ください:

「[c] oo」で検索すると見つかりました。

69
prinzdezibel

最初のC++コンパイラ(「クラスを持つC」)は実際にCコードを生成するので、間違いなく実行可能です。

基本的に、基本クラスは構造体です。派生構造体は、最初の位置にベース構造体を含める必要があります。そのため、「派生」構造体へのポインターもベース構造体への有効なポインターになります。

typedef struct {
   data member_x;
} base;

typedef struct {
   struct base;
   data member_y;
} derived;

void function_on_base(struct base * a); // here I can pass both pointers to derived and to base

void function_on_derived(struct derived * b); // here I must pass a pointer to the derived class

関数は構造体の一部として関数ポインターとして使用できるため、p-> call(p)のような構文が可能になりますが、構造体へのポインターを関数自体に明示的に渡す必要があります。

59
UncleZeiv

一般的なアプローチは、関数へのポインタで構造体を定義することです。これは、任意のタイプで呼び出すことができる「メソッド」を定義します。次に、サブタイプはこの共通構造に独自の機能を設定し、それを返します。

たとえば、Linuxカーネルには次の構造体があります。

struct inode_operations {
    int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
    struct dentry * (*lookup) (struct inode *,struct dentry *, 
                               struct nameidata *);
    ...
};

登録された各タイプのファイルシステムは、createlookup、および残りの関数に対して独自の関数を登録します。残りのコードでは、一般的なinode_operationsを使用できます。

struct inode_operations   *i_op;
i_op -> create(...);
51

C++はCからそれほど遠くありません。

クラスは、VTableと呼ばれる関数ポインターのテーブルへの隠されたポインターを持つ構造です。 Vtable自体は静的です。型が同じ構造のVtablesを指しているが、ポインターが他の実装を指している場合、ポリモーフィズムが得られます。

コードの混乱を避けるために、構造体をパラメーターとして取る関数に呼び出しロジックをカプセル化することをお勧めします。

また、構造体のインスタンス化と初期化を関数(C++コンストラクターと同等)および削除(C++のデストラクター)でカプセル化する必要があります。とにかくこれらは良い習慣です。

typedef struct
{
   int (*SomeFunction)(TheClass* this, int i);
   void (*OtherFunction)(TheClass* this, char* c);
} VTable;

typedef struct
{
   VTable* pVTable;
   int member;

} TheClass;

メソッドを呼び出すには:

int CallSomeFunction(TheClass* this, int i)
{
  (this->pVTable->SomeFunction)(this, i);
}
29

私は他のみんなの答えを見て、これを思いつきました:

#include <stdio.h>

typedef struct
{
    int (*get)(void* this);
    void (*set)(void* this, int i);
    int member;

} TheClass;

int Get(void* this)
{
    TheClass* This = (TheClass*)this;
    return This->member;
}

void Set(void* this, int i)
{
    TheClass* This = (TheClass*)this;
    This->member = i;
}

void init(TheClass* this)
{
    this->get = &Get;
    this->set = &Set;
}

int main(int argc, char **argv)
{
    TheClass name;
    init(&name);
    (name.set)(&name, 10);
    printf("%d\n", (name.get)(&name));
    return 0;
}

それがいくつかの質問に答えることを望みます。

15
red_hax0r

記事の付録B Open Reusable Object Models 、Ian PiumartaおよびAlessandro Warthによる [〜#〜] vpri [〜#〜] は、オブジェクトモデルの実装です。 GNU C、約140行のコード。これは魅力的な読み物です!

GNU Cへの拡張(statement expression))を使用して、オブジェクトにメッセージを送信するマクロのキャッシュされていないバージョンを次に示します:

struct object;

typedef struct object *oop; 
typedef oop *(*method_t)(oop receiver, ...);

//...

#define send(RCV, MSG, ARGS...) ({ \ 
    oop r = (oop)(RCV); \ 
    method_t method = _bind(r, (MSG)); \ 
    method(r, ##ARGS); \ 
}) 

同じドキュメントで、objectvtablevtable_delegatedおよびsymbol構造体、および_bindおよびvtable_lookup 関数。

乾杯!

12

ファイル関数fopen、fclose、freadは、CのOOコードの例です。クラスのプライベートデータの代わりに、データとC関数をカプセル化するために使用されるFILE構造で機能しますメンバークラス関数として機能します http://www.Amazon.com/File-Structures-Object-Oriented-Approach-C/dp/0201874016

1
Priyank Bolia
#include <stdio.h>

typedef struct {
    int  x;
    int z;
} base;

typedef struct {
    base;
    int y;
    int x;
} derived;

void function_on_base( base * a) // here I can pass both pointers to derived and to base
{
    printf("Class base [%d]\n",a->x);
    printf("Class base [%d]\n",a->z);
}
void function_on_derived( derived * b) // here I must pass a pointer to the derived class
{
    printf("Class derived [%d]\n",b->y);
    printf("Class derived [%d]\n",b->x);
}

int main()
{
    derived d;
    base b;
    printf("Teste de poliformismo\n");

    b.x = 2;
    d.y = 1;
    b.z = 3;
    d.x = 4;
    function_on_base(&b);
    function_on_base(&d);
    function_on_derived(&b);
    function_on_derived(&d);
    return 0;
}

出力は次のとおりです。

Class base [3]
Class base [1]
Class base [4]
Class derived [2]
Class derived [3]
Class derived [1]
Class derived [4]

動作します、そのポリモーフィックコード。

UncleZeivは最初にそれについて説明しました。

1
Paulo Silva

ウィキペディアから:プログラミング言語と型理論では、ポリモーフィズム(ギリシャ語のπολύς、polys、 "many、much"およびμορφή、morphē、 "form、shape")は、さまざまなタイプのエンティティに対する単一のインターフェイスの提供です。

したがって、Cで実装する唯一の方法は、(半)自動型情報管理とともに可変引数を使用することです。たとえば、C++では次のように書くことができます(些細なことでごめんなさい):

void add( int& result, int a1, int a2 );
void add( float& result, float a1, float a2 );
void add( double& result, double a1, double a2 );

Cでは、他のソリューションの中でも、できることは次のようなものです。

int int_add( int a1, int a2 );
float float_add( float a1, fload a2 );
double double_add( double a1, double a2 );

void add( int typeinfo, void* result, ... );

次に必要なもの:

  1. 列挙型/マクロで「typeinfo」を実装する
  2. stdarg.hで後者の関数を実装するには
  3. cの静的型チェックに別れを告げる

ポリモーフィズムの他の実装は、まさにこのように見えるはずです。上記の答えは、代わりに、ポリモーフィズムよりも継承に対処しようとしているようです!

0
EnzoR

CでOO機能をビルドするためにも、以前の回答を見ることができます。

しかし、C言語の例で、ポリモーフィズムとは何かを理解したい場合(この質問にリダイレクトされた他の質問で尋ねられたように)。間違っているかもしれませんが、Cポインターの算術ほど理解しやすいものは考えられません。私の意見では、ポインターの算術演算は本質的にCでは多態的です。次の例では、同じ関数(OOのメソッド)、つまり加算(+)は、入力構造のプロパティに応じて異なる動作を生成します。

例:

double a*;
char str*;

a=(double*)malloc(2*sizeof(double));
str=(char*)malloc(2*sizeof(char)); 

a=a+2; // make the pointer a, point 2*8 bytes ahead.

str=str+2; // make the pointer str, point 2*1 bytes ahead.

免責事項:私はCに非常に慣れており、修正されることを非常に楽しみにしており、他のユーザーのコメントから学ぶか、間違っている場合はこの回答を完全に消去します。どうもありがとう、

0
johnnybegood