C言語はC++オブジェクト向け言語の多態特性を実現する

5390 ワード

C++言語の進化の過程で、多くの新しい特性を加えて、現代の高級OO言語になりました.当初C++は1970年代末、80年代初めにC++の発展が始まったばかりで、その時C++はC with classと見なすことができ、C++が書いたコードはC言語コードに変換された後、Cコンパイラを経てターゲットコードを生成した.今、C++言語はすでに1セットの体系を形成して、自分の言語の書く風格と設計の理念があります.C++全体の構文体系には、C++ベース(C言語の構文特性、OO特性に互換性)、テンプレートライブラリ&STL、汎用プログラミングなどの知識が含まれています.C++学習時間のコストが高いため(プログラミング言語ランキングを見れば分かるように、この言語の流行度は低下しており、最近流行しているpythonやjavascriptなどの他の言語に追われている)、一般の人はその大部分の特性を熟練してマスターするには3~5年かかります.実は今大部分のブランド会社は現在C with Classの段階にとどまって、簡単に言えばC++という言語で製品類のパッケージとフレームワークを構築して、その他の特性も使用することを推薦しないで、更にC++11とC++17の後で新しく増加した特性は言うまでもありません.
ここで私は簡単にC++言語に対する自分の見方を表明します:個人の学習コストと時間コストは今のプロジェクト開発の中で考慮しなければならない問題で、既存のコンピュータアーキテクチャの下で、CPUの実行効率とメモリ空間の大きさは一般的に開発のボトルネックではありません(一部の組み込み分野を除きます).プロジェクトを開発する際に、運用効率と開発時間の長さを選ぶなら、一般の会社は開発時間の効率の高い言語を第一にしているに違いない.C++は別の統一路線を歩み、一つの言語ですべてのことをしようとした結果、言語がますます肥大化し、運行効率の面ではC言語に及ばず、開発効率は現在のjava、pythonなどの言語に追いつかなかった.この間、社内のネット上で牛人がブログを書いて、C++とCのつながりと違いを深く検討しました.C++言語の内部モジュールの結合が強すぎます.つまり、この特性を使っていなくても、C++がリンクするとき、言語モジュールの内部依存性の問題でターゲットコードにコンパイルされます.簡単なhello worldプログラムで、C++ターゲットファイルのサイズはC言語より十数K大きくなります.現在、一般的に成熟した製品は、効率が高いことを考慮すると、nginx、redis、libeventなど、直接C言語で書かれています.たとえばクラスUnixオペレーティングシステム(linux)のほとんどのコードもC言語で書かれています.現在流行しているいくつかの言語は、学習コストが低く、入手が速いため、多くのプログラマーに受け入れられている.もちろん私が今知っている限りでは、多くのゲーム会社の駆動エンジンはC++で書かれており、一部の大型組み込み製品もC++で作ることを考えています.同時に現在、市場にも良い第三者C++ライブラリがあります.例えば、boost、QT、ACEなどのライブラリです.私はQTで多くのクライアントプログラムを書いて、会社のルータ製品のテストとメンテナンスをサポートしました.特にQTにQtQuickの特性を導入した後、開発時間も大幅に向上しました.会社が25~30%の時間コストを節約するのに役立ちました.
次に本題に戻ります.C++言語の基本文法には、パッケージ、継承、マルチステートの3つの特性が含まれています.C++が文法的にこれらのオブジェクト向け特性をサポートしているからこそ、大規模なプロジェクトはこの言語でプラットフォームや製品フレームワークを構築することを考慮します.実はこのように言うことができて、対象に向かうのは過程に向かうことに対して、更に1種の思惟の方式で、世の中の万物はすべて対象で、ピタゴラス学派が宣伝しているように感じます:万物はすべて数えます.したがって,オブジェクト向けはソフトウェア設計方式として(もちろんオブジェクト向けには独特のSOLID設計原則がある),他の言語は完全に借りることができ,例えばC言語でよい.linuxカーネルコードでは、オブジェクト向けの特性がよく見られます.たとえば、ファイル操作では、属性とメソッド(一般的に関数ポインタとして定義される)を特定の構造体にカプセル化します.C++はCと同じように静的言語であり、コードがコンパイルされると、その関数アドレスが相対的に固定されていることを知っています.それはどのように実行することで関数の動作を変えるのでしょうか.C++教科書では,多態は虚関数によって実現されると紹介されている.ベースクラスのいくつかの関数の前にvirtualと宣言された場合、C++コンパイラはメモリオブジェクトモデルの前に虚関数ポインタを挿入します.このポインタは虚関数テーブルを指します.虚関数ポインタが指す関数テーブルのアドレスを変更して、そのマルチステート操作を実現できるからです.以下では,C++の継承と多状態特性を簡単な例で実現する.
/*
 *
 *C    C++        
 *  Created on: 2015 12 26 
 *      Author: @CodingGeek
 */
#include 
#include 

struct base;

struct base_vtbl
{
	void(*dance)(struct base *);
	void(*jump)(struct base*, int high);
};

struct base
{
	struct base_vtbl *vptr;
	/*base members */
};

void base_dance(struct base *pb)
{
	pb->vptr->dance(pb);
}

void base_jump(struct base *pb, int high)
{
	pb->vptr->jump(pb, high);
}

void based_dance(struct base *pb)
{
	printf("base dance
"); } void based_jump(struct base *pb, int high) { printf("base jump:%d
", high); } /* global vtable for base */ struct base_vtbl base_table = { based_dance, based_jump }; void base_init(struct base *pb) { pb->vptr = &base_table; } struct derived1 { struct base super; /*derived members */ }; void derived1_dance(struct derived1 *pd) { /*implementation of derived1's dance function */ printf("derived1 dance
"); } void derived1_jump(struct derived1 *pd, int high) { /*implementation of derived1's jump function */ printf("derived1 jump:%d
", high); } /*global vtable for derived1 */ struct base_vtbl derived1_table = { (void(*)(struct base *))&derived1_dance, (void(*)(struct base*, int))&derived1_jump }; void derived1_init(struct derived1 *pd) { pd->super.vptr = &derived1_table; /*init base members d->super.foo */ /*init derived1 members d->foo */ } struct derived2 { struct base super; /*derived2 members */ }; void derived2_dance(struct derived2 *pd) { /*implementation of derived2's dance function*/ printf("derived2 dance
"); } void derived2_jump(struct derived2 *pd, int high) { /*implementation of derived2's jump function */ printf("derived2 jump:%d
", high); } /*global vtable for derived2 */ struct base_vtbl derived2_table = { (void(*)(struct base *))&derived2_dance, (void(*)(struct base*, int))&derived2_jump }; void derived2_init(struct derived2 *pd) { pd->super.vptr = &derived2_table; /*init base members d->super.foo */ /*init derived1 members d->foo */ } int main(void) { /*OK~! We're done with our declarations, now we can finally do * some polymorphism in C */ /* C++ , , */ struct base b; base_init(&b); struct base *b_ptr = (struct base*)&b; base_dance(b_ptr); base_jump(b_ptr, 99); struct derived1 d1; derived1_init(&d1); struct derived2 d2; derived2_init(&d2); /* ,C , , C++ * reinterpret_cast, C++ static_cast dynamic_cast */ struct base *b1_ptr = (struct base *)&d1; struct base *b2_ptr = (struct base *)&d2; /* */ base_dance(b1_ptr); /*calls derived1_dance */ base_jump(b1_ptr, 88);/*calls derived1_jump */ base_dance(b2_ptr); /*calls derived2_dance */ base_jump(b2_ptr, 77); /*calls derived2_jump */ return 0; }

以上の実行結果から,C言語による多態化の特性は問題なく,抽象モデル(実際にはC++メモリオブジェクトモデルに類似)をどのように構築するかという問題がある.C++はちょうど原生的にこのすべてを支持して、私たちが繰り返し車輪を作る必要はありません.