C++言語はvirtual
関数を提供します。純粋なC言語の実装の制約内で、同様の効果をどのように達成できますか?
4 に答える
ここから盗まれました。
C++ クラスから
class A {
protected:
int a;
public:
A() {a = 10;}
virtual void update() {a++;}
int access() {update(); return a;}
};
C コードフラグメントを派生させることができます。の 3 つの C++ メンバー関数は、class A
アウトオブライン (スタンドアロン) コードを使用して書き直され、アドレスによって という名前の構造体に集められますA_functable
。のデータ メンバA
と関数テーブルを結合して、 という名前の C 構造体にしA
ます。
struct A;
typedef struct {
void (*A)(struct A*);
void (*update)(struct A*);
int (*access)(struct A*);
} A_functable;
typedef struct A{
int a;
A_functable *vmt;
} A;
void A_A(A *this);
void A_update(A* this);
int A_access(A* this);
A_functable A_vmt = {A_A, A_update, A_access};
void A_A(A *this) {this->vmt = &A_vmt; this->a = 10;}
void A_update(A* this) {this->a++;}
int A_access(A* this) {this->vmt->update(this); return this->a;}
/*
class B: public A {
public:
void update() {a--;}
};
*/
struct B;
typedef struct {
void (*B)(struct B*);
void (*update)(struct B*);
int (*access)(struct A*);
} B_functable;
typedef struct B {
A inherited;
} B;
void B_B(B *this);
void B_update(B* this);
B_functable B_vmt = {B_B, B_update, A_access};
void B_B(B *this) {A_A(this); this->inherited.vmt = &B_vmt; }
void B_update(B* this) {this->inherited.a--;}
int B_access(B* this) {this->inherited.vmt->update(this); return this->inherited.a;}
int main() {
A x;
B y;
A_A(&x);
B_B(&y);
printf("%d\n", x.vmt->access(&x));
printf("%d\n", y.inherited.vmt->access(&y));
}
必要以上に精巧ですが、要点はわかります。
@GCC....仮想関数は、オブジェクトの基本クラスで宣言され、サブクラスで「オーバーライド」または実装されます。つまり、Vehicle Base クラスがあり、Motorcycle と Automobile という 2 つのサブクラスを作成するとします。ベース クラスは AddTires() の仮想関数を宣言し、サブ クラスはこの関数を実装し、各サブ クラスはそれを異なる方法で実装します。車には 4 つの車輪があり、オートバイには 2 つの車輪があります。ただし、C や C++ の構文はわかりません。お役に立てれば
これが仮想関数とは何かの説明です。
Cには継承の概念がないため、プレーンCで仮想関数を実装する方法はありません。
更新: 以下のコメントで説明されているように、構造体と関数ポインターを使用して、ストレートCの仮想関数と同様のことを行うことができます。ただし、「真の」仮想関数を持つC ++のような言語に慣れている場合は、C近似がはるかにエレガントでなく、使いにくいことに気付くでしょう。
仮想関数は、C++ のオブジェクト指向の機能です。それらは、現在持ち歩いているタイプではなく、特定のオブジェクト インスタンスに依存するメソッドを参照します。
つまり、オブジェクトを Bar としてインスタンス化し、それを Foo にキャストすると、仮想メソッドはインスタンス化時のもののまま (Bar で定義) になり、他のメソッドは Foo からのものになります。
仮想関数は通常、vtables を介して実装されます (これについては、さらに調査する必要があります ;))。
構造体を貧弱なオブジェクトとして使用し、それらに関数ポインターを保存することにより、Cで同様のことをシミュレートできます。
(より正確には、非仮想関数はメソッドがどのクラスから取得されるべきかを曖昧にしますが、実際には C++ は現在の型を使用していると思います。)