C++の解明 - クラス

From emmtrix Wiki
Jump to navigation Jump to search
This page is a translated version of the page Demystifying C++ - Classes and the translation is 100% complete.
Other languages:

クラスはC++プログラミングの基石であり、現代の複雑なソフトウェアアプリケーションの構造化に不可欠なオブジェクト指向パラダイムを体現しています。C++では、クラスは属性とメソッドを通じてデータと振る舞いをカプセル化します。対照的に、アセンブラやCは主にデータ構造と関数に依存しています。

メソッド

C++コンパイラと同様に、トランスパイラはクラスをCの構造体と関数に変換します。クラスのメソッドは、クラスを表す構造体への"this"ポインタを取る関数に変換されます。属性にアクセスする際には、thisポインタが自動的に使用され、他のメソッドを呼び出す際には、それが単に渡されます。

メソッド
class C1 {
  int attr;
public:  
  void onChangeAttr();

void setAttr(int v) {
    attr = v;
    onChangeAttr();
  }
};
struct C1 {
  int attr;
};

void C1_onChangeAttr(struct C1* this);

void C1_setAttr(struct C1* this, int v) {
  this->attr = v;
  C1_onChangeAttr(this);
}

継承

C++では、継承はクラスが基底クラスから属性やメソッドを継承することを可能にする中心的な原則です。しかし、Cは自然に継承をサポートしていません。そのため、C++からCへのトランスパイラは、派生構造体の各基底クラスをその独自のフィールドとして埋め込むことで、継承関係をシミュレートする特別な技術を使用します。

継承の重要な側面は、基底クラス(Base)と派生クラス(Derived)間のキャスティングです:

  • 派生クラスから基底クラスへのキャスト: アセンブラでは、このようなキャストは定数値の加算に対応します。これは、派生構造体の該当するフィールドにアクセスすることでCで比較的簡単に実装できます。これは、基底クラスのメソッドを呼び出す場合や属性にアクセスする場合など、C++の多くの場所で暗黙的に発生します。
  • 静的な基底クラスから派生クラスへのキャスト: アセンブラでは、このようなキャストは定数値の減算に対応します。これはCでより複雑であり、いくつかのCキャスティングが必要です。
  • 動的な基底クラスから派生クラスへのキャスト: これらはdynamic_castキーワードで生成され、実装がより複雑です。これはランタイム時の型チェックが必要です。動的キャストについては、仮想クラスに関する章で議論されます。
基底クラスの表現
class Derived : public Base1, public Base2 {
  ...
};
struct Derived {
  struct Base1 base1;
  struct Base2 base2;

  ...
};
派生クラスから基底クラスへのキャスト
static_cast<Base2*>(derived)
&derived->base2
静的な基底クラスから派生クラスへのキャスト
static_cast<Derived*>(base2)
(Derived*)((char*)base2 - offsetof(Derived, base2))

コンストラクタとデストラクタ

コンストラクタとデストラクタは、オブジェクトの自動初期化とクリーンアッププロセスを管理するため、C++プログラミングにおいて中心的な役割を果たします。コンストラクタはオブジェクトが作成されるときに自動的に呼び出される特別なクラスメソッドです。オブジェクト属性の初期値を定義したり、他の初期化タスクを実行するためによく使用されます。デストラクタはコンストラクタの反対であり、オブジェクトが破棄されるときに呼び出され、メモリや他のリソースを解放するようなクリーンアップタスクに最適です。

Cでは、コンストラクタとデストラクタの直接的な対応物はありません。代わりに、オブジェクトの作成や破棄時に呼び出される通常の関数に置き換えられます。完全性のために、Itanium ABIには、少なくとも2つのコンストラクタ関数(ctor_completector_base)およびデストラクタ関数(dtor_completedtor_basedtor_delete)が生成されることを述べておくべきです。completeバリアントは通常使用され、baseバリアントはコンストラクタ/デストラクタ内の基底クラスに必要であり、dtor_deleteは仮想デストラクタを持つクラスにdeleteを適用するときに使用されます。

コンストラクタ
class C1 {
  // Attributes
public:
  C1() {
    // Initialization code
  }

  ~C1() {
    // Cleanup code
  }
};
struct C1 {
  // Attributes
};

void C1_ctor_complete(struct C1* this) {
  // Initialization code
}

void C1_dtor_complete(struct C1* this) {
  // Cleanup code
}
スタック変数
{
  C1 obj;

  ...
}
{
  struct C1 obj;
  C1_ctor_complete(&obj);

  ...

  C1_dtor_complete(&obj);
}