深さ探索C++対象モデルノートの四――関数語意学(下)
4.2仮想メンバー関数
虚関数の一般的な実装モデル:
1.
クラスごとにダミーテーブルがあります
(
virtual table
)、クラスを含む
active
虚関数のアドレス.
2.
各クラスのオブジェクトには
vptr
を選択します.
内
active
虚関数は次のとおりです.
1.
このクラスで定義された虚関数エンティティは、書き換えられます(
overriding
)存在する可能性のあるベースクラス虚関数エンティティ
2.
派生クラスがベースクラスの虚関数を上書きしないことを決定した場合に発生する、ベースクラスから継承される虚関数エンティティ
3.
1つ
pure_virtual_called()
関数エンティティ.
純虚関数の空間保衛者は、実行期間異常処理関数としてもよい
にある
C++
で表示される
public base class
のポインタまたは参照、アドレス
derived class object
”
1つを識別する
class
マルチステートをサポートするかどうか、唯一の適切な方法は、虚関数があるかどうかを見ることです.
class
マルチステートをサポートするために、この追加の実行期間情報が必要な虚関数があります.
1
)単一継承での虚関数
次のコードの場合、虚表レイアウトは図のようになります.
4.1.
次の文の場合:
ptr->z();
コンパイル時に虚関数の呼び出しを設定するのに十分な知識がある方法(すなわち、コンパイラがどのように変換するか).
1.
一般的には知らない
ptr
オブジェクトの真のタイプを指すが、経由することを知っている.
ptr
オブジェクトにアクセスできるダミーテーブル(
virtual table
)
2.
どれなのかわからない
z()
関数エンティティは呼び出されますが、それぞれがわかります.
z()
関数アドレスはすべて
slot 4
上記の情報に基づいて、コンパイラは呼び出しを次のように変換します.
( *ptr->vptr[4] )( ptr );
実行期間中にのみ認識できるのは、次のとおりです.
slot 4
どれを指しているのか
z()
関数エンティティ.
2
)多重継承下の虚関数
多重継承の下で虚関数をサポートし、その複雑さは2番目と後続のベースクラスにあり、実行期間の調整が必要である.
this
針.
class Base1 {
public:
Base1();
virtual ~Base1();
virtual void speakClearly();
virtual Base1 *clone() const;
protected:
float data_Base1;
};
class Base2 {
public:
Base2();
virtual ~Base2();
virtual void mumble();
virtual Base2 *clone() const;
protected:
float data_Base2;
};
class Derived : public Base1, public Base2 {
public:
Derived();
virtual ~Derived();
virtual Derived *clone() const;
protected:
float data_Derived;
};
多重継承の下で、派生クラスには
n-1
個の追加
virtual table
,
n
前のベースクラスの数を表します.
上記について
Derived
クラス、2つあります
virtual table
コンパイル:
1.
メインテーブル
vtbl__Derived
と、
Base1
(左端ベースクラス)共有
2.
サブテーブル
vtbl__Base2__Derived
と、
Base2
(2番目のベースクラス)関連
君が一人
Derived
オブジェクトアドレスを1つに指定
Base1
ポインタまたは
Derived
ポインタの場合、処理される
virtual table
メインテーブル
vtbl__Derived
.
君が一人
Derived
オブジェクトアドレスを1つに指定
Base2
ポインタの場合、処理される
virtual table
サブテーブル
vtbl__Base2__Derived
.
ダミーテーブルのレイアウトを図のようにする
4.2
.
3つの場合、2番目または後続のベースクラスが虚関数のサポートに影響します.
1つ目のケース:派生クラス虚関数は、「2番目または後続のベースクラスを指す」ポインタで呼び出されます.
例:
Base2 *ptr = new Derived;
//
よびだし
Derived::~Derived
//ptr
後ろに調整しなければならない
sizeof( Base1 )
バイト
Derived
オブジェクトの開始
delete ptr;
2つ目のケース:派生クラスを指すポインタによって、2番目または後続のベースクラスから継承された虚関数が呼び出されます.
例:
Derived *pder = new Derived;
//
よびだし
Base2::mumble()
//pder
前向きに調整しなければならない
sizeof( Base1 )
バイト
Base2 subobject
の開始点
pder->mumble();
3つ目は、「2番目または後続のベースクラス」のポインタによって呼び出されます.
clone
()
で行ないます.
Base2 *pb1 = new Derived;
//
よびだし
Derived* Derived::clone()
//
戻り値は、
Base2 subobject
Base2 *pb2 = pb1->clone();
3
)仮想継承下の虚関数
複雑です.
4.3関数の効率
略
4.4メンバー関数へのポインタ
1.
非静的メンバー関数のアドレスを取り、虚関数でない場合はメモリ内の真のアドレスを取得します.しかし、この値も不完全であり、関数を呼び出すにはクラスオブジェクトアドレスにバインドする必要があります.
2.
ひとつ取る
静的メンバー関数のアドレスは、メモリ内のアドレスであり、そのアドレスのタイプは「クラスメンバー関数へのポインタ」ではなく、
「非メンバー関数ポインタ」です.(参照)
4.1
)
3.
非静的メンバー関数のアドレスを取ります.虚関数である場合は、後述します.
しこう
member function
のポインタの宣言構文と、
member selection
演算子のポインタは、
this
ポインタのスペース保持者.だから
static member function
(ない
this
ポインタ)のタイプは、関数ポインタであり、ポインタではありません.
member function
を選択します.
1つのメンバー関数ポインタを使用すると、虚関数、多重継承、虚ベースクラスなどに使用されない場合、一般的なポインタよりもコストが高くなりません.
1
)虚関数へのポインタ
1つの虚関数に対してアドレスを取って、得ることができるのはそれが
virtual table
に表示されます.
2
)複数と虚の継承によるメンバー関数へのポインタ
のために
メンバー関数のポインタも
サポート
多重と虚の継承、
Stroustrup
以下の構造を設計した.
4.5インライン関数
一般にインライン関数の処理には2つの段階がある
1
・関数定義を分析し、関数の内部連結の資本を決定する.インライン不可と判断すると静的関数に変換されます.
2
.本格的なインライン関数の拡張操作は呼び出しポイントにあり、パラメータ評価操作と一時オブジェクト管理をもたらす.
1
)形式パラメータ
インライン拡張の間、何が起こりましたか?
1.
実際のパラメータが定数式である場合、置換する前に評価操作を完了できます.に続く
inline
代わりに定数を直接縛ることができます
2.
実際のパラメータが副作用をもたらす場合は、一時オブジェクトを導入する必要があります.
3.
定数式でも副作用式でもない場合は、直接置き換えます.
2
)ローカル変数
一般に、インライン関数の各ローカル変数は、関数呼び出しの閉じた区間に配置され、ユニークな名前を持つ必要があります.
インライン関数が単一の式で複数回拡張される場合、ローカル変数のセットが毎回必要になります.複数の式を分離して複数回拡張すると,局所変数のセットだけで繰り返し使用できる.
虚関数の一般的な実装モデル:
1.
クラスごとにダミーテーブルがあります
(
virtual table
)、クラスを含む
active
虚関数のアドレス.
2.
各クラスのオブジェクトには
vptr
を選択します.
内
active
虚関数は次のとおりです.
1.
このクラスで定義された虚関数エンティティは、書き換えられます(
overriding
)存在する可能性のあるベースクラス虚関数エンティティ
2.
派生クラスがベースクラスの虚関数を上書きしないことを決定した場合に発生する、ベースクラスから継承される虚関数エンティティ
3.
1つ
pure_virtual_called()
関数エンティティ.
純虚関数の空間保衛者は、実行期間異常処理関数としてもよい
にある
C++
で表示される
public base class
のポインタまたは参照、アドレス
derived class object
”
1つを識別する
class
マルチステートをサポートするかどうか、唯一の適切な方法は、虚関数があるかどうかを見ることです.
class
マルチステートをサポートするために、この追加の実行期間情報が必要な虚関数があります.
1
)単一継承での虚関数
次のコードの場合、虚表レイアウトは図のようになります.
4.1.
class Point {
public:
virtual ~Point();
virtual Point& mult( float ) = 0;
// ... other operations ...
float x() const { return _x; }
virtual float y() const { return 0; }
virtual float z() const { return 0; }
// ...
protected:
Point( float x = 0.0 );
float _x;
};
class Point2d : public Point {
public:
Point2d( float x = 0.0, float y = 0.0 )
: Point( x ), _y( y ) {}
~Point2d();
// overridden base class virtual functions
Point2d& mult( float );
float y() const { return _y; }
// ... other operations ...
protected:
float _y;
};
class Point3d: public Point2d {
public:
Point3d( float x = 0.0,
float y = 0.0, float z = 0.0 )
: Point2d( x, y ), _z( z ) {}
~Point3d();
// overridden base class virtual functions
Point3d& mult( float );
float z() const { return _z; }
// ... other operations ...
protected:
float _z;
};
次の文の場合:
ptr->z();
コンパイル時に虚関数の呼び出しを設定するのに十分な知識がある方法(すなわち、コンパイラがどのように変換するか).
1.
一般的には知らない
ptr
オブジェクトの真のタイプを指すが、経由することを知っている.
ptr
オブジェクトにアクセスできるダミーテーブル(
virtual table
)
2.
どれなのかわからない
z()
関数エンティティは呼び出されますが、それぞれがわかります.
z()
関数アドレスはすべて
slot 4
上記の情報に基づいて、コンパイラは呼び出しを次のように変換します.
( *ptr->vptr[4] )( ptr );
実行期間中にのみ認識できるのは、次のとおりです.
slot 4
どれを指しているのか
z()
関数エンティティ.
2
)多重継承下の虚関数
多重継承の下で虚関数をサポートし、その複雑さは2番目と後続のベースクラスにあり、実行期間の調整が必要である.
this
針.
class Base1 {
public:
Base1();
virtual ~Base1();
virtual void speakClearly();
virtual Base1 *clone() const;
protected:
float data_Base1;
};
class Base2 {
public:
Base2();
virtual ~Base2();
virtual void mumble();
virtual Base2 *clone() const;
protected:
float data_Base2;
};
class Derived : public Base1, public Base2 {
public:
Derived();
virtual ~Derived();
virtual Derived *clone() const;
protected:
float data_Derived;
};
多重継承の下で、派生クラスには
n-1
個の追加
virtual table
,
n
前のベースクラスの数を表します.
上記について
Derived
クラス、2つあります
virtual table
コンパイル:
1.
メインテーブル
vtbl__Derived
と、
Base1
(左端ベースクラス)共有
2.
サブテーブル
vtbl__Base2__Derived
と、
Base2
(2番目のベースクラス)関連
君が一人
Derived
オブジェクトアドレスを1つに指定
Base1
ポインタまたは
Derived
ポインタの場合、処理される
virtual table
メインテーブル
vtbl__Derived
.
君が一人
Derived
オブジェクトアドレスを1つに指定
Base2
ポインタの場合、処理される
virtual table
サブテーブル
vtbl__Base2__Derived
.
ダミーテーブルのレイアウトを図のようにする
4.2
.
3つの場合、2番目または後続のベースクラスが虚関数のサポートに影響します.
1つ目のケース:派生クラス虚関数は、「2番目または後続のベースクラスを指す」ポインタで呼び出されます.
例:
Base2 *ptr = new Derived;
//
よびだし
Derived::~Derived
//ptr
後ろに調整しなければならない
sizeof( Base1 )
バイト
Derived
オブジェクトの開始
delete ptr;
2つ目のケース:派生クラスを指すポインタによって、2番目または後続のベースクラスから継承された虚関数が呼び出されます.
例:
Derived *pder = new Derived;
//
よびだし
Base2::mumble()
//pder
前向きに調整しなければならない
sizeof( Base1 )
バイト
Base2 subobject
の開始点
pder->mumble();
3つ目は、「2番目または後続のベースクラス」のポインタによって呼び出されます.
clone
()
で行ないます.
Base2 *pb1 = new Derived;
//
よびだし
Derived* Derived::clone()
//
戻り値は、
Base2 subobject
Base2 *pb2 = pb1->clone();
3
)仮想継承下の虚関数
複雑です.
4.3関数の効率
略
4.4メンバー関数へのポインタ
1.
非静的メンバー関数のアドレスを取り、虚関数でない場合はメモリ内の真のアドレスを取得します.しかし、この値も不完全であり、関数を呼び出すにはクラスオブジェクトアドレスにバインドする必要があります.
2.
ひとつ取る
静的メンバー関数のアドレスは、メモリ内のアドレスであり、そのアドレスのタイプは「クラスメンバー関数へのポインタ」ではなく、
「非メンバー関数ポインタ」です.(参照)
4.1
)
3.
非静的メンバー関数のアドレスを取ります.虚関数である場合は、後述します.
しこう
member function
のポインタの宣言構文と、
member selection
演算子のポインタは、
this
ポインタのスペース保持者.だから
static member function
(ない
this
ポインタ)のタイプは、関数ポインタであり、ポインタではありません.
member function
を選択します.
1つのメンバー関数ポインタを使用すると、虚関数、多重継承、虚ベースクラスなどに使用されない場合、一般的なポインタよりもコストが高くなりません.
1
)虚関数へのポインタ
1つの虚関数に対してアドレスを取って、得ることができるのはそれが
virtual table
に表示されます.
2
)複数と虚の継承によるメンバー関数へのポインタ
のために
メンバー関数のポインタも
サポート
多重と虚の継承、
Stroustrup
以下の構造を設計した.
// ,
struct __mptr {
int delta; // this offset
int index;
union {
ptrtofunc faddr; // index -1 , virtual //table
int v_offset; // ( ) vptr
};
};
4.5インライン関数
一般にインライン関数の処理には2つの段階がある
1
・関数定義を分析し、関数の内部連結の資本を決定する.インライン不可と判断すると静的関数に変換されます.
2
.本格的なインライン関数の拡張操作は呼び出しポイントにあり、パラメータ評価操作と一時オブジェクト管理をもたらす.
1
)形式パラメータ
インライン拡張の間、何が起こりましたか?
1.
実際のパラメータが定数式である場合、置換する前に評価操作を完了できます.に続く
inline
代わりに定数を直接縛ることができます
2.
実際のパラメータが副作用をもたらす場合は、一時オブジェクトを導入する必要があります.
3.
定数式でも副作用式でもない場合は、直接置き換えます.
2
)ローカル変数
一般に、インライン関数の各ローカル変数は、関数呼び出しの閉じた区間に配置され、ユニークな名前を持つ必要があります.
インライン関数が単一の式で複数回拡張される場合、ローカル変数のセットが毎回必要になります.複数の式を分離して複数回拡張すると,局所変数のセットだけで繰り返し使用できる.