C++が恐ろしい言語であり、Cでオブジェクト指向プログラムを作成する方法について話している人(私はLinus Torvaldsだったと思う)について少し前に読んだことを覚えています。 allオブジェクト指向の概念はCにも引き継がれます。例えば:
sizeof
パラメーターに応じてブードゥー教を行うことができますカプセル化と継承をどのようにエミュレートしますか?
プライベートメンバーを格納するネストされた構造を持つことで、カプセル化をエミュレートできると思います。回避するのはかなり簡単ですが、おそらくPRIVATE
または、構造体の外部から使用することを意図していないことを示す明確な名前を付けることができます。継承はどうですか?
通常の関数と仮想テーブル(vtable)を使用してポリモーフィズムを実装できます。これが、プログラミング演習のために(C++に基づいて)発明した非常にきちんとしたシステムです。
コンストラクターはメモリを割り当て、メモリが初期化されるクラスのinit関数を呼び出します。各初期化関数には、仮想関数ポインター(純粋仮想の場合はNULL)を含む静的vtable構造体も含める必要があります。派生クラスの初期化関数は、他の処理を行う前にスーパークラスの初期化関数を呼び出します。
次のように、仮想関数ラッパー(vtableが指す関数と混同しないでください)を実装することにより、非常に素晴らしいAPIを作成できます(add static inline
ヘッダーの前でこれを行う場合):
int playerGuess(Player* this) { return this->vtable->guess(this); }
構造体のバイナリレイアウトを乱用することで、単一の継承を行うことができます。
階層のタイプ間でキャストする場合、ポインター値を調整する必要があることが多いため、多重継承は厄介であることに注意してください。
他のタイプ固有のデータも仮想テーブルに追加できます。例には、実行時の型情報(文字列としての型名など)、スーパークラスvtableおよびデストラクタチェーンへのリンクが含まれます。おそらく、派生クラスデストラクタがオブジェクトをスーパークラスに降格し、ベースクラスデストラクタに到達して最終的に構造体を解放するまで、そのクラスのデストラクタを再帰的に呼び出す仮想デストラクタが必要です。
カプセル化 player_protected.hで構造体を定義し、player_protected.cで関数(vtableが指す)を実装することで、派生クラスについても同様に実装しましたが、これは非常に不器用でパフォーマンスが低下します(仮想ラッパーをヘッダーに配置できないため) )、だから私はそれに対してお勧めします。
あなたは主題に関する「聖書」を読みましたか? Object Oriented C ...を参照してください.
少し高い高度から、OOPメインストリームが示唆するよりも、よりオープンマインドな問題を考えると、オブジェクト指向プログラミングは、オブジェクトを関連する機能を持つデータとして考えることを意味します。関数は、オブジェクトに物理的にアタッチする必要があることを意味します。これは、C++など、OOPのパラダイムをサポートする一般的な言語の場合と同様です。
struct T
{
int data;
int get_data() const { return data; }
};
GTK + Object and Type System を詳しく調べることをお勧めします。これはOOP Cプログラミング言語で実現された素晴らしい例です:
GTK +は独自のカスタムオブジェクトシステムを実装し、継承や仮想関数などの標準的なオブジェクト指向機能を提供します
協会はまた契約的であり、従来のものであり得る。
カプセル化とデータ隠蔽のテクニックに関して、一般的でシンプルなものは Opaque Pointer (またはOpaque Data Type)です。それを渡すことはできますが、情報をロードまたは保存するには、関連する関数を呼び出す必要がありますその不透明なポインターの後ろに隠れているオブジェクトと通信する方法を知っています。
もう1つは似ていますが、違いは Shadow Data type -このリンクを確認してください Jon Jagger は、あまり知られていない技術の優れた説明を提供します。
AppleのCベースのCoreFoundationフレームワークは、実際にはその「オブジェクト」がObjective-Cのオブジェクト、実際のOO言語です。フレームワークのかなり大きなサブセットがApple site as CF-Lite 。この方法で行われた主要なOSレベルのフレームワークで役立つケーススタディになるかもしれません。
間違いなくObjective-Cを見てください。
typedef struct objc_object {
Class isa;
} *id;
typedef struct objc_class {
struct objc_class *isa;
struct objc_class *super_class
const char *name;
long version;
long info
long instance_size;
struct objc_ivar_list *ivars;
struct objc_method_list **methodLists;
struct objc_cache *cache;
struct objc_protocol_list *protocols;
} *Class;
ご覧のとおり、継承情報は他の詳細とともにクラス構造体に保持されています(クラスをオブジェクトとして扱うこともできます)。
Objective-Cは、変数をパブリックに宣言する必要があるという点で、C++と同じようにカプセル化されています。ストレートCは、モジュールのみが内部アクセスできるvoidポインターを返すことができるという点ではるかに柔軟性が高いため、その点でカプセル化の方がはるかに優れています。
グラフィックコースの一部として基本的なOOスタイルCペイントプログラムを書いたことがあります-クラス宣言までは行かず、vtableポインターを最初の要素として使用しましたこのような低レベルでvtableを操作することの素晴らしい点は、いくつかのポインターを変更するか、オブジェクトクラスを動的に変更することで、実行時にクラスの動作を変更できることです。ハイブリッドオブジェクトの種類、偽の多重継承など.
継承パターンの例については、LinuxカーネルでVFSレイヤーがどのように機能するかを見てください。さまざまなファイルシステムのファイル操作は、一連の汎用ファイル操作関数(たとえば、generic_file_aio_read()
、generic_file_llseek()
...)を「継承」しますが、独自の実装(たとえば、 ntfs_file_aio_write()
)。
Objective-Cに関する素晴らしい記事とディスカッションはこちら:
http://cocoawithlove.com/2009/10/objective-c-niche-why-it-survives-in.html
gtkおよびglibライブラリは、マクロを使用してオブジェクトをさまざまな型にキャストします。
add_widget(GTK_WIDGET(myButton));
それがどのように行われたか言うことはできませんが、そのソースを読んで、それがどのように行われたかを正確に知ることができます。
継承を処理する1つの方法は、ネストされた構造を持つことです。
struct base
{
...
};
void method_of_base(base *b, ...);
struct child
{
struct base base_elements;
...
};
その後、次のような呼び出しを行うことができます。
struct child c;
method_of_base(&c.b, ...);
Cでのオブジェクト指向プログラミングの優れた例については、数年前のPOV-Rayのソースをご覧ください-バージョン3.1gは特に優れています。もちろん、「オブジェクト」は関数ポインターを使用した構造体です。マクロは、抽象オブジェクトのコアメソッドとデータを提供するために使用され、派生クラスはそのマクロで始まる構造体でした。ただし、プライベート/パブリックに対処する試みはありませんでした。多くの例外を除いて、見られるものは.hファイルにあり、実装の詳細は.cファイルにありました。
関数ポインタを再割り当てするだけで、あるクラスを別の似たようなクラスにオンザフライで変換するなど、C++にどのように引き継ぐことができるかわからないいくつかの巧妙なトリックがありました。今日の動的言語に対応しています。詳細を忘れました。私はそれがCSGの交差オブジェクトと結合オブジェクトだったと思う.
あなたはObjective-Cを見てみたいかもしれません、それはそれがほとんど何をするかです。これは、Objective-C OOコードをCにコンパイルするフロントエンドです。