回答:
はい。実際、Axel Schreinerは彼の著書「ANSI-Cでのオブジェクト指向プログラミング」を無料で提供しており、この主題を非常に徹底的にカバーしています。
あなたは多態性について話しているので、はい、できます。C++が登場する数年前に、私たちはそのようなことを行っていました。
基本的には、a struct
を使用して、データとそのデータに関連する関数を指す関数ポインターのリストの両方を保持します。
したがって、通信クラスでは、次のようなオブジェクトのデータとともに、構造内の4つの関数ポインターとして維持されるオープン、読み取り、書き込み、クローズの呼び出しがあります。
typedef struct {
int (*open)(void *self, char *fspec);
int (*close)(void *self);
int (*read)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
int (*write)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
// And data goes here.
} tCommClass;
tCommClass commRs232;
commRs232.open = &rs232Open;
: :
commRs232.write = &rs232Write;
tCommClass commTcp;
commTcp.open = &tcpOpen;
: :
commTcp.write = &tcpWrite;
もちろん、上記のコードセグメントは実際にはなどの「コンストラクタ」内にありrs232Init()
ます。
そのクラスから「継承」するときは、ポインタを変更して独自の関数を指すようにします。これらの関数を呼び出したすべての人は、関数ポインタを介してそれを実行し、ポリモーフィズムを提供します。
int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");
手動のvtableのようなものです。
ポインタをNULLに設定することで仮想クラスを作成することもできます。動作はC ++とは少し異なります(コンパイル時のエラーではなく、実行時のコアダンプ)。
これを示すサンプルコードの一部を次に示します。まずトップレベルのクラス構造:
#include <stdio.h>
// The top-level class.
typedef struct sCommClass {
int (*open)(struct sCommClass *self, char *fspec);
} tCommClass;
次に、TCP「サブクラス」の関数があります。
// Function for the TCP 'class'.
static int tcpOpen (tCommClass *tcp, char *fspec) {
printf ("Opening TCP: %s\n", fspec);
return 0;
}
static int tcpInit (tCommClass *tcp) {
tcp->open = &tcpOpen;
return 0;
}
そして、HTTPのものも:
// Function for the HTTP 'class'.
static int httpOpen (tCommClass *http, char *fspec) {
printf ("Opening HTTP: %s\n", fspec);
return 0;
}
static int httpInit (tCommClass *http) {
http->open = &httpOpen;
return 0;
}
そして最後に、それを実際に見せるためのテストプログラム:
// Test program.
int main (void) {
int status;
tCommClass commTcp, commHttp;
// Same 'base' class but initialised to different sub-classes.
tcpInit (&commTcp);
httpInit (&commHttp);
// Called in exactly the same manner.
status = (commTcp.open)(&commTcp, "bigiron.box.com:5000");
status = (commHttp.open)(&commHttp, "http://www.microsoft.com");
return 0;
}
これは出力を生成します:
Opening TCP: bigiron.box.com:5000
Opening HTTP: http://www.microsoft.com
これで、サブクラスに応じて、さまざまな関数が呼び出されていることがわかります。
tCommClass
名前はに変更されtCommVT
、tCommClass
構造体にはデータフィールドとtCommVT vt
「唯一の」仮想テーブルを指す単一のフィールドのみが含まれます。各インスタンスですべてのポインタを運ぶと、不要なオーバーヘッドが追加され、C ++やIMHOよりもJavaScriptで行う方法に似ています。
名前空間は多くの場合、次のように実行されます。
stack_push(thing *)
の代わりに
stack::push(thing *)
作成するにはCのようなものの中に構造体をC ++あなたが変えることができますクラス:
class stack {
public:
stack();
void push(thing *);
thing * pop();
static int this_is_here_as_an_example_only;
private:
...
};
に
struct stack {
struct stack_type * my_type;
// Put the stuff that you put after private: here
};
struct stack_type {
void (* construct)(struct stack * this); // This takes uninitialized memory
struct stack * (* operator_new)(); // This allocates a new struct, passes it to construct, and then returns it
void (*push)(struct stack * this, thing * t); // Pushing t onto this stack
thing * (*pop)(struct stack * this); // Pops the top thing off the stack and returns it
int this_is_here_as_an_example_only;
}Stack = {
.construct = stack_construct,
.operator_new = stack_operator_new,
.push = stack_push,
.pop = stack_pop
};
// All of these functions are assumed to be defined somewhere else
そして、やります:
struct stack * st = Stack.operator_new(); // Make a new stack
if (!st) {
// Do something about it
} else {
// You can use the stack
stack_push(st, thing0); // This is a non-virtual call
Stack.push(st, thing1); // This is like casting *st to a Stack (which it already is) and doing the push
st->my_type.push(st, thing2); // This is a virtual call
}
デストラクタや削除は行いませんでしたが、同じパターンに従っています。
this_is_here_as_an_example_onlyは静的クラス変数のようなもので、型のすべてのインスタンス間で共有されます。すべてのメソッドは本当に静的ですが、一部はthis *
st->my_type->push(st, thing2);
代わりにst->my_type.push(st, thing2);
struct stack_type my_type;
代わりにstruct stack_type * my_type;
Class
構造体はどうですか?それはOO Cはなるだろうより C ++よりもダイナミック。どのようにそのことについて?ちなみに+1。
CでOOPを実装することは、それ自体が有用であるだけでなく、OOPを学び、その内部の仕組みを理解するための優れた方法であると私は信じています。多くのプログラマーの経験は、技術を効率的かつ自信を持って使用するために、プログラマーは、基本的な概念が最終的にどのように実装されるかを理解する必要があることを示しています。Cでクラス、継承、およびポリモーフィズムをエミュレートすると、まさにこれがわかります。
元の質問に答えるために、CでOOPを実行する方法を教えるいくつかのリソースを次に示します。
EmbeddedGurus.comのブログ記事「Cでのオブジェクトベースのプログラミング」は、ポータブルCでクラスと単一継承を実装する方法を示しています。http: //embeddedgurus.com/state-space/2008/01/object-based-programming-in-c /
アプリケーションノート「 "C +" — Cでのオブジェクト指向プログラミング」は、プリプロセッサマクロを使用して、クラス、単一継承、および遅延バインディング(ポリモーフィズム)をCに実装する方法を示しています:http : //www.state-machine.com/resources/cplus_3。 0_manual.pdf、サンプルコードはhttp://www.state-machine.com/resources/cplus_3.0.zipから入手できます。
私はそれが終わったのを見てきました。私はそれをお勧めしません。C ++は最初、この方法を、中間ステップとしてCコードを生成するプリプロセッサーとして開始しました。
基本的に、最終的に行うことは、関数参照を格納するすべてのメソッドのディスパッチテーブルを作成することです。クラスを派生させるには、このディスパッチテーブルをコピーし、オーバーライドするエントリを置き換える必要があります。新しい「メソッド」は、ベースメソッドを呼び出す場合、元のメソッドを呼び出す必要があります。結局、あなたはC ++を書き直すことになります。
glib
Cで客観的に書かれていませんか?
動物と犬の簡単な例:C ++のvtableメカニズムをミラーリングします(とにかく)。また、割り当てとインスタンス化(Animal_Alloc、Animal_New)を分離して、malloc()を複数回呼び出さないようにします。また、明示的にthis
ポインタを渡す必要があります。
非仮想関数を実行するとしたら、それは些細なことです。それらをvtableに追加しないでくださいthis
。静的関数はポインターを必要としません。多重継承では、あいまいさを解決するために複数のvtableが一般に必要です。
また、setjmp / longjmpを使用して例外処理を実行できる必要があります。
struct Animal_Vtable{
typedef void (*Walk_Fun)(struct Animal *a_This);
typedef struct Animal * (*Dtor_Fun)(struct Animal *a_This);
Walk_Fun Walk;
Dtor_Fun Dtor;
};
struct Animal{
Animal_Vtable vtable;
char *Name;
};
struct Dog{
Animal_Vtable vtable;
char *Name; // Mirror member variables for easy access
char *Type;
};
void Animal_Walk(struct Animal *a_This){
printf("Animal (%s) walking\n", a_This->Name);
}
struct Animal* Animal_Dtor(struct Animal *a_This){
printf("animal::dtor\n");
return a_This;
}
Animal *Animal_Alloc(){
return (Animal*)malloc(sizeof(Animal));
}
Animal *Animal_New(Animal *a_Animal){
a_Animal->vtable.Walk = Animal_Walk;
a_Animal->vtable.Dtor = Animal_Dtor;
a_Animal->Name = "Anonymous";
return a_Animal;
}
void Animal_Free(Animal *a_This){
a_This->vtable.Dtor(a_This);
free(a_This);
}
void Dog_Walk(struct Dog *a_This){
printf("Dog walking %s (%s)\n", a_This->Type, a_This->Name);
}
Dog* Dog_Dtor(struct Dog *a_This){
// Explicit call to parent destructor
Animal_Dtor((Animal*)a_This);
printf("dog::dtor\n");
return a_This;
}
Dog *Dog_Alloc(){
return (Dog*)malloc(sizeof(Dog));
}
Dog *Dog_New(Dog *a_Dog){
// Explict call to parent constructor
Animal_New((Animal*)a_Dog);
a_Dog->Type = "Dog type";
a_Dog->vtable.Walk = (Animal_Vtable::Walk_Fun) Dog_Walk;
a_Dog->vtable.Dtor = (Animal_Vtable::Dtor_Fun) Dog_Dtor;
return a_Dog;
}
int main(int argc, char **argv){
/*
Base class:
Animal *a_Animal = Animal_New(Animal_Alloc());
*/
Animal *a_Animal = (Animal*)Dog_New(Dog_Alloc());
a_Animal->vtable.Walk(a_Animal);
Animal_Free(a_Animal);
}
PS。これはC ++コンパイラでテストされていますが、Cコンパイラで動作させるのは簡単です。
typedef
内部のstruct
C.は不可能である
これは興味深い内容です。私は同じ質問を自分で考えていましたが、それについて考えることの利点は次のとおりです。
非OOP言語でOOPの概念を実装する方法を想像してみると、OOp言語(私の場合はC ++)の長所を理解するのに役立ちます。これにより、特定の種類のアプリケーションでCを使用するかC ++を使用するかについて、より良い判断を下すことができます。
これに関する情報と意見をウェブで閲覧したところ、組み込みプロセッサ用のコードを書いていて、Cコンパイラしか利用できない作成者を見つけました。http: //www.eetimes.com/discussion/other/4024626/Object-Oriented -C-Creating-Foundation-Classes-Part-1
彼の場合、プレーンなCでOOPの概念を分析し、適合させることは有効な追求でした。Cで実装しようとしたことによるパフォーマンスオーバーヘッドの影響により、OOPの概念を犠牲にしていたようです。
私が受けた教訓は、確かにある程度は可能であり、そうすることを試みるにはいくつかの正当な理由があります。
結局、マシンはスタックポインタビットをいじるので、プログラムカウンタはジャンプしてメモリアクセス操作を計算します。効率の観点からは、プログラムで行われるこれらの計算の数が少ないほど良いですが、人為的エラーの影響を最小限に抑える方法でプログラムを編成できるように、場合によってはこの税金を支払う必要があります。OOP言語コンパイラは、両方の側面を最適化するよう努めています。プログラマーは、これらの概念をCなどの言語で実装する場合、より注意深く行う必要があります。
Core FoundationのAPIセットについては、Appleのドキュメントを参照すると役立つ場合があります。純粋なC APIですが、タイプの多くはObjective-Cオブジェクトの同等物にブリッジされます。
また、Objective-C自体の設計を見ることも役立つでしょう。C ++とは少し異なりobjc_msg_send
ます。たとえば、オブジェクトのメソッドを呼び出すなど、C関数の観点からオブジェクトシステムが定義されている点です。コンパイラは角括弧構文をこれらの関数呼び出しに変換するので、それを知る必要はありませんが、質問を考えると、内部でどのように機能するかを学ぶのに役立つことがあります。
使用できるテクニックはいくつかあります。最も重要なのは、プロジェクトを分割する方法です。.hファイルで宣言されているプロジェクトのインターフェイスと、.cファイルのオブジェクトの実装を使用します。重要な部分は、.hファイルを含むすべてのモジュールはオブジェクトのみをとしてvoid *
認識し、.cファイルは構造の内部を知っている唯一のモジュールであることです。
例としてFOOという名前のクラスの場合、次のようになります。
.hファイル
#ifndef FOO_H_
#define FOO_H_
...
typedef struct FOO_type FOO_type; /* That's all the rest of the program knows about FOO */
/* Declaration of accessors, functions */
FOO_type *FOO_new(void);
void FOO_free(FOO_type *this);
...
void FOO_dosomething(FOO_type *this, param ...):
char *FOO_getName(FOO_type *this, etc);
#endif
C実装ファイルはそのようなものになります。
#include <stdlib.h>
...
#include "FOO.h"
struct FOO_type {
whatever...
};
FOO_type *FOO_new(void)
{
FOO_type *this = calloc(1, sizeof (FOO_type));
...
FOO_dosomething(this, );
return this;
}
そのため、そのモジュールのすべての関数にオブジェクトへのポインターを明示的に与えます。C ++コンパイラはそれを暗黙的に行い、Cでは明示的に書き出します。
私は実際this
にプログラムで使用して、プログラムがC ++でコンパイルされないようにし、構文強調表示エディターで別の色になるという優れた特性を備えています。
FOO_structのフィールドは1つのモジュールで変更することができ、別のモジュールはまだ使用可能であるために再コンパイルする必要さえありません。
そのスタイルでは、OOP(データのカプセル化)の利点の大部分をすでに処理しています。関数ポインタを使用することで、継承などを実装することも簡単ですが、正直なところ、それが実際に役立つことはほとんどありません。
typedef struct FOO_type FOO_type
、ヘッダ内の空隙へのtypedefのではなく、まだあなたの構造を露出していない間、あなたは、型チェックの追加利益を得ます。
関数ポインターを使用してそれを偽造することができ、実際、C ++プログラムをCにコンパイルすることは理論的には可能だと思います。
ただし、パラダイムを使用する言語を選択するのではなく、言語にパラダイムを強制することはほとんど意味がありません。
オブジェクト指向Cを実行できます。韓国でそのタイプのコードが生産されているのを見たことがあります。これは、私が何年も目にした中で最も恐ろしいモンスターでした(これは、昨年(2007)のようにコードを見たものです)。ですから、そうすることができます。そして、以前にもそうしたことがある人もいますし、今でもこのようにしています。ただし、C ++またはObjective-Cをお勧めします。どちらもCから生まれた言語であり、オブジェクト指向をさまざまなパラダイムで提供することを目的としています。
OOPアプローチが解決しようとしている問題よりも優れていると確信している場合、なぜ非OOP言語で解決しようとしているのですか?ジョブに間違ったツールを使用しているようです。C ++またはその他のオブジェクト指向のCバリアント言語を使用します。
Cで記述された既存の大規模なプロジェクトでコーディングを開始するために質問する場合は、独自の(または他の誰かの)OOPパラダイムをプロジェクトのインフラストラクチャに強制しようとしないでください。プロジェクトにすでに存在するガイドラインに従ってください。一般に、クリーンなAPIと分離されたライブラリーおよびモジュールは、クリーンなOOP- ish設計を実現するのに大いに役立ちます。
結局のところ、あなたが本当にOOP Cを始めることにしているなら、これ(PDF)を読んでください。
はい、できます。C ++やObjective-Cが登場する前に、人々はオブジェクト指向のCを書いていました。C ++とObjective-Cはどちらも、部分的には、Cで使用されているOOの概念のいくつかを取り入れ、言語の一部としてそれらを形式化しようとする試みでした。
これは、メソッド呼び出しのように見える、またはメソッド呼び出しであるものを作成する方法を示す非常に単純なプログラムです(これを行うためのより良い方法があります。これは、言語が概念をサポートすることの証明にすぎません)。
#include<stdio.h>
struct foobarbaz{
int one;
int two;
int three;
int (*exampleMethod)(int, int);
};
int addTwoNumbers(int a, int b){
return a+b;
}
int main()
{
// Define the function pointer
int (*pointerToFunction)(int, int) = addTwoNumbers;
// Let's make sure we can call the pointer
int test = (*pointerToFunction)(12,12);
printf ("test: %u \n", test);
// Now, define an instance of our struct
// and add some default values.
struct foobarbaz fbb;
fbb.one = 1;
fbb.two = 2;
fbb.three = 3;
// Now add a "method"
fbb.exampleMethod = addTwoNumbers;
// Try calling the method
int test2 = fbb.exampleMethod(13,36);
printf ("test2: %u \n", test2);
printf("\nDone\n");
return 0;
}
追加する小さなOOCコード:
#include <stdio.h>
struct Node {
int somevar;
};
void print() {
printf("Hello from an object-oriented C method!");
};
struct Tree {
struct Node * NIL;
void (*FPprint)(void);
struct Node *root;
struct Node NIL_t;
} TreeA = {&TreeA.NIL_t,print};
int main()
{
struct Tree TreeB;
TreeB = TreeA;
TreeB.FPprint();
return 0;
}
私はこれを1年間掘り続けてきました:
GObjectシステムは純粋なCでは使いづらいので、CでOOスタイルを簡単にするために、いくつかの素晴らしいマクロを書いてみました。
#include "OOStd.h"
CLASS(Animal) {
char *name;
STATIC(Animal);
vFn talk;
};
static int Animal_load(Animal *THIS,void *name) {
THIS->name = name;
return 0;
}
ASM(Animal, Animal_load, NULL, NULL, NULL)
CLASS_EX(Cat,Animal) {
STATIC_EX(Cat, Animal);
};
static void Meow(Animal *THIS){
printf("Meow!My name is %s!\n", THIS->name);
}
static int Cat_loadSt(StAnimal *THIS, void *PARAM){
THIS->talk = (void *)Meow;
return 0;
}
ASM_EX(Cat,Animal, NULL, NULL, Cat_loadSt, NULL)
CLASS_EX(Dog,Animal){
STATIC_EX(Dog, Animal);
};
static void Woof(Animal *THIS){
printf("Woof!My name is %s!\n", THIS->name);
}
static int Dog_loadSt(StAnimal *THIS, void *PARAM) {
THIS->talk = (void *)Woof;
return 0;
}
ASM_EX(Dog, Animal, NULL, NULL, Dog_loadSt, NULL)
int main(){
Animal *animals[4000];
StAnimal *f;
int i = 0;
for (i=0; i<4000; i++)
{
if(i%2==0)
animals[i] = NEW(Dog,"Jack");
else
animals[i] = NEW(Cat,"Lily");
};
f = ST(animals[0]);
for(i=0; i<4000; ++i) {
f->talk(animals[i]);
}
for (i=0; i<4000; ++i) {
DELETE0(animals[i]);
}
return 0;
}
これが私のプロジェクトサイトです(en。docを書く時間がありませんが、中国語のdocの方がはるかに優れています)。
ジムラーソンの1996年の講演「セクション312プログラミングランチタイムセミナー:高低レベルC」でのCを使用した継承の例があります。
CでOOPの概念を使用するのに適した記事または本はどれですか。
デイブハンソンのCインタフェースと実装がある優れたカプセル化と命名上と関数ポインタの使用に非常に良いです。Daveは継承をシミュレートしようとしません。
OOPは、データをプログラムのコードよりも重要視するパラダイムにすぎません。OOPは言語ではありません。したがって、プレーンCが単純な言語であるように、プレーンCのOOPも単純です。
あなたがしたいかもしれない一つのことは、X Window用のXtツールキットの実装を調べることです。確かに長くなっていきますが、使用されている構造の多くは、従来のC内でオブジェクト指向の方法で機能するように設計されています。一般に、これはあちこちに間接層を追加し、互いに重ねる構造を設計することを意味します。
このようにCに配置されたOOの方法で本当に多くのことを行うことができます。たとえそれが時々そのように感じられても、OOの概念がの精神から完全に形成されたわけではありません#include<favorite_OO_Guru.h>
。それらは、当時確立されたベストプラクティスの多くを実際に構成していました。オブジェクト指向の言語とシステムは、その日のプログラミングの時代精神の一部のみを抽出して増幅しました。
質問への答えは「はい、できます」です。
オブジェクト指向C(OOC)キットは、オブジェクト指向の方法でプログラミングしたいが、古き良きCにもこだわる人向けです。OOCは、クラス、単一および多重継承、例外処理を実装します。
特徴
•Cマクロと関数のみを使用し、言語拡張は必要ありません。(ANSI-C)
•アプリケーションの読みやすいソースコード。物事をできるだけ簡単にするために注意が払われました。
•クラスの単一継承
•インターフェイスとミックスインによる多重継承(バージョン1.3以降)
•例外の実装(純粋なCで!)
•クラスの仮想関数
•クラス実装を容易にする外部ツール
詳細については、http://ooc-coding.sourceforge.net/にアクセスしてください。
人々はCを使用してC ++スタイルをエミュレートしようとしているようです。私の考えでは、オブジェクト指向プログラミングを行うことは、Cが実際には構造指向プログラミングを行うことです。ただし、遅延バインディング、カプセル化、継承などを実現できます。継承の場合は、基本構造へのポインターをサブ構造で明示的に定義します。これは明らかに多重継承の形式です。また、
//private_class.h
struct private_class;
extern struct private_class * new_private_class();
extern int ret_a_value(struct private_class *, int a, int b);
extern void delete_private_class(struct private_class *);
void (*late_bind_function)(struct private_class *p);
//private_class.c
struct inherited_class_1;
struct inherited_class_2;
struct private_class {
int a;
int b;
struct inherited_class_1 *p1;
struct inherited_class_2 *p2;
};
struct inherited_class_1 * new_inherited_class_1();
struct inherited_class_2 * new_inherited_class_2();
struct private_class * new_private_class() {
struct private_class *p;
p = (struct private_class*) malloc(sizeof(struct private_class));
p->a = 0;
p->b = 0;
p->p1 = new_inherited_class_1();
p->p2 = new_inherited_class_2();
return p;
}
int ret_a_value(struct private_class *p, int a, int b) {
return p->a + p->b + a + b;
}
void delete_private_class(struct private_class *p) {
//release any resources
//call delete methods for inherited classes
free(p);
}
//main.c
struct private_class *p;
p = new_private_class();
late_bind_function = &implementation_function;
delete_private_class(p);
でコンパイルしc_compiler main.c inherited_class_1.obj inherited_class_2.obj private_class.obj
ます。
したがって、アドバイスは純粋なCスタイルに固執することであり、C ++スタイルを強制しようとするのではありません。また、この方法は、APIを構築する非常にクリーンな方法に役立ちます。
CでのOOPのさらに別の工夫については、http://slkpg.byethost7.com/instance.htmlを参照してください。これは、ネイティブCだけを使用して再入可能にするためにインスタンスデータを強調します。多重継承は、関数ラッパーを使用して手動で行われます。タイプセーフが維持されます。ここに小さなサンプルがあります:
typedef struct _peeker
{
log_t *log;
symbols_t *sym;
scanner_t scan; // inherited instance
peek_t pk;
int trace;
void (*push) ( SELF *d, symbol_t *symbol );
short (*peek) ( SELF *d, int level );
short (*get) ( SELF *d );
int (*get_line_number) ( SELF *d );
} peeker_t, SlkToken;
#define push(self,a) (*self).push(self, a)
#define peek(self,a) (*self).peek(self, a)
#define get(self) (*self).get(self)
#define get_line_number(self) (*self).get_line_number(self)
INSTANCE_METHOD
int
(get_line_number) ( peeker_t *d )
{
return d->scan.line_number;
}
PUBLIC
void
InitializePeeker ( peeker_t *peeker,
int trace,
symbols_t *symbols,
log_t *log,
list_t *list )
{
InitializeScanner ( &peeker->scan, trace, symbols, log, list );
peeker->log = log;
peeker->sym = symbols;
peeker->pk.current = peeker->pk.buffer;
peeker->pk.count = 0;
peeker->trace = trace;
peeker->get_line_number = get_line_number;
peeker->push = push;
peeker->get = get;
peeker->peek = peek;
}
私はパーティーに少し遅れましたが、私はトピックに関する私の経験を共有したいと思います:私は最近埋め込まれたもので作業し、私が持っている唯一の(信頼できる)コンパイラはCなので、オブジェクト指向を適用したいと思いますCで書かれた私の組み込みプロジェクトでのアプローチ
これまでに見たほとんどのソリューションはタイプキャストを多用しているため、タイプセーフが失われます。間違いを犯した場合、コンパイラーは役に立ちません。これはまったく受け入れられません。
私が持っている要件:
この記事では、私のアプローチについて詳しく説明しました。Cでのオブジェクト指向プログラミング。さらに、基本クラスと派生クラスのボイラープレートコードを自動生成するためのユーティリティがあります。
私はそれを試してみた小さなライブラリを構築しました。だから私は経験を共有すると思いました。
https://github.com/thomasfuhringer/oxygen
単一継承は、構造体を使用して非常に簡単に実装でき、それを他のすべての子クラスに拡張できます。親構造への単純なキャストにより、すべての子孫で親メソッドを使用できます。変数がこの種のオブジェクトを保持する構造体を指すことがわかっている限り、いつでもルートクラスにキャストしてイントロスペクションを実行できます。
すでに述べたように、仮想メソッドはややトリッキーです。しかし、彼らは実行可能です。物事をシンプルに保つために、クラスの説明構造で関数の配列を使用します。これは、すべての子クラスが必要に応じて個々のスロットをコピーして再投入します。
多重継承は実装がかなり複雑で、パフォーマンスに大きな影響を与えます。だから私はそれを残します。私は、実際の状況をきれいにモデル化することが非常に多くの場合に望ましいと考えていますが、おそらく90%のケースでは、単一継承がニーズをカバーしています。また、単一継承は単純であり、コストはかかりません。
また、タイプセーフについても気にしません。プログラミングの間違いを防ぐためにコンパイラーに依存すべきではないと私は思います。とにかく、それはエラーのかなり小さな部分からのみあなたを守ります。
通常、オブジェクト指向の環境では、参照カウントを実装して、可能な限りメモリ管理を自動化することもできます。そのため、参照カウントを「オブジェクト」ルートクラスに入れ、ヒープメモリの割り当てと割り当て解除をカプセル化するいくつかの機能を追加しました。
それはすべて非常にシンプルで無駄のないものであり、C ++であるモンスターに対処することを強制することなく、OOの本質を与えてくれます。そして、私はCランドに留まるという柔軟性を保持しています。これにより、とりわけ、サードパーティのライブラリーの統合が容易になります。
CのスーパーセットであるObjective-Cの使用を提案します。
Objective-Cは30年前のものですが、エレガントなコードを書くことができます。
はい。しかし、Cを使用してポリモーフィズムを実装しようとする人を見たことがありません。