最初は、これを行う方法がわかりませんでした。結局のところ、Cはオブジェクト指向プログラミング言語ではなく、継承というものはありません。 しかし、私はすでにCの経験があり、仮想関数がどのように機能するかを知っていたので、構造(構造)を使用して仮想関数の動作を模倣する方法があるはずだと考えました。
仮想機能とは何かを知らない人のための簡単な説明:
仮想関数は、独自の明確な実装を持つように後継クラスによってオーバーライドできる関数です。 C ++は、仮想関数テーブルなどのメカニズムを使用します
(簡単にvtable)実行時のリンクをサポートするため。 仮想テーブルは、各仮想関数に対して、継承階層内のこの関数の最も近い実装へのポインターを格納する静的配列です。 階層内で最も近い実装は、オブジェクトのメソッドテーブルから関数アドレスを取得することにより、実行時に決定されます。
C ++で仮想関数を使用する簡単な例を見てみましょう
class ClassA { public: ClassA() {data = 10;} virtual void set() { std::cout << "ClassA is increasing" << std::endl; data++; } int get() { set(); return data; } protected: int data; }; class ClassB : public ClassA { public: void set() { std::cout << "ClassB is decreasing" << std::endl; data--; } };
上記の例では、
get()
と
set()
2つのメソッドを持つ
ClassA
クラスがあります。
get()
メソッドは仮想関数としてマークされています。
ClassB
クラスでは、その実装が変更されています。
data
整数は
protected
キーワードでマークされているため、
ClassB
下位クラスはそれにアクセスできます。
int main() { ClassA classA; ClassB classB; std::cout << "ClassA value: " << classA.get() << std::endl; std::cout << "ClassB value: " << classB.get() << std::endl; return 0; }
結論:
ClassA is increasing ClassA value: 11 ClassB is decreasing ClassB value: 9
Cで仮想関数の概念を実装する方法を考えてみましょう。仮想関数はポインターとして表され、vtableに格納され、vtableは静的配列であるため、ClassAクラス自体、ClassAの仮想関数テーブルをシミュレートする構造を作成する必要があります。 ClassAメソッドの実装。
/* */ struct ClassA; /* , . */ typedef struct { void (*ClassA)(struct ClassA*); /* "" */ void (*set)(struct ClassA*); /* set */ int (*get)(struct ClassA*); /* get */ } ClassA_functiontable; typedef struct ClassA { int data; ClassA_functiontable *vtable; /* ClassA */ } ClassA; /* ClassA */ void ClassA_constructor(ClassA *this); void ClassA_set(ClassA *this); int ClassA_get(ClassA *this); /* ClassA */ ClassA_functiontable ClassA_vtable = {ClassA_constructor, ClassA_set, ClassA_get }; /* */ void ClassA_constructor(ClassA *this) { this->vtable = &ClassA_vtable; this->data = 10; } void ClassA_set(ClassA *this) { printf("ClassA is increasing\n"); this->data++; } int ClassA_get(ClassA *this) { this->vtable->set(this); return this->data; }
Cでは、呼び出し元を指す
this
ポインターはありません。 C ++で
this
ポインターの使用をシミュレートするために、
this
ようなパラメーターに名前を付けました(さらに、C ++でのオブジェクトメソッド呼び出しの実際の動作のように見えます)。
上記のコードからわかるように、
ClassA_get()
実装は、vtableからのポインターを介して
set()
関数を呼び出します。 次に、派生クラスの実装を見てみましょう。
/* */ struct ClassB; /* , , */ typedef struct { void (*ClassB)(struct ClassB*); void (*set)(struct ClassB*); void (*get)(struct ClassA*); } ClassB_functiontable; typedef struct ClassB { ClassA inherited_class; } ClassB; void ClassB_constructor(ClassB *this); void ClassB_set(ClassB *this); int ClassB_get(ClassB *this); ClassB_functiontable ClassB_vtable = {ClassB_constructor, ClassB_set, ClassB_get}; void ClassB_constructor(ClassB *this) { /* */ ClassA_constructor((ClassA*)this); /* */ this->inherited_class.vtable = (ClassA_functiontable*)&ClassB_vtable; } void ClassB_set(ClassB *this) { printf("ClassB decreasing\n"); this->inherited_class.data--; } int ClassB_get(ClassB *this) { this->inherited_class.vtable->set((ClassA*)this); return this->inherited_class.data; }
コードからわかるように、vtableを使用してClassBの
get()
実装から
set()
関数を呼び出します。vtableは目的の
set()
関数を指し、「継承された」ClassAクラスを通じて同じ整数
data
アクセスします。
これは、
main()
関数の外観です。
int main() { ClassA classA; ClassB classB; ClassA_constructor(&classA); ClassB_constructor(&classB); printf("ClassA value: %d\n", classA.vtable->get(&classA)); /* get() - */ printf("ClassB value: %d\n", classB.inherited_class.vtable->get((struct ClassA*)&classB)); }
結論:
ClassA is increasing ClassA value: 11 ClassB decreasing ClassB value: 9
もちろん、このトリックはC ++や他のオブジェクト指向プログラミング言語のように自然に見えず、Cでプログラムを書いたときにこのようなものを実装する必要はありませんでしたが、それでも内部構造をよりよく理解するのに役立ちます仮想関数。
翻訳者から: C ++での仮想関数の内部実装に関する詳細な記事