9

http://www.cplusplus.com/doc/tutorial/polymorphism.html(読みやすくするために編集)のポリモーフィズムの例を次に示します。

// abstract base class
#include <iostream>
using namespace std;

class Polygon {
    protected:
        int width;
        int height;
    public:
        void set_values(int a, int b) { width = a; height = b; }
        virtual int area(void) =0;
};

class Rectangle: public Polygon {
    public:
        int area(void) { return width * height; }
};

class Triangle: public Polygon {
    public:
        int area(void) { return width * height / 2; }
};

int main () {
    Rectangle rect;
    Triangle trgl;
    Polygon * ppoly1 = &rect;
    Polygon * ppoly2 = &trgl;
    ppoly1->set_values (4,5);
    ppoly2->set_values (4,5);
    cout << ppoly1->area() << endl; // outputs 20
    cout << ppoly2->area() << endl; // outputs 10
    return 0;
}

私の質問は、コンパイラがppoly1がRectangleであり、ppoly2がTriangleであることをどのように認識して、正しいarea()関数を呼び出すことができるかということです。「Polygon*ppoly1 =▭」の行を見て、rectが長方形であることを知ることでそれを見つけることができますが、それはすべての場合に機能するとは限りません。このようなことをした場合はどうなりますか?

cout << ((Polygon *)0x12345678)->area() << endl;

そのランダムなメモリ領域へのアクセスが許可されていると仮定します。

これをテストしますが、現在使用しているコンピューターではテストできません。

(私は明らかな何かを見逃していないことを願っています...)

4

6 に答える 6

25

各オブジェクト(少なくとも1つの仮想関数を持つクラスに属する)には、と呼ばれるポインターがありますvptr。これは、実際のクラスのを指しvtblます(仮想関数を持つ各クラスには、少なくとも1つ、場合によっては複数の継承シナリオでは複数のクラスがあります)。

には、仮想関数ごとに1つずつ、多数のvtblポインターが含まれています。したがって、実行時に、コードはオブジェクトを使用してvptrを見つけ、vtblそこから実際にオーバーライドされた関数のアドレスを見つけます。

特定のケースでPolygonRectangle、、、およびTriangleそれぞれにがあり、それぞれに関連するメソッドvtblを指す1つのエントリがあります。'sを指すようになり、同様に'sareappoly1指すvptrようになります。お役に立てれば!Rectanglevtblppoly2Trianglevtbl

于 2008-10-14T22:46:42.170 に答える
6

ChrisJester-Youngがこの質問に対する基本的な答えを示しています。

ウィキペディアには、より詳細な扱いがあります。

このタイプのものがどのように機能するか(および複数の仮想継承を含むすべてのタイプの継承)の詳細を知りたい場合は、StanLippmanの「InsidetheC ++ ObjectModel」が最適なリソースの1つです。

于 2008-10-14T22:52:25.310 に答える
3

バインディングの側面を無視すると、これを決定するのは実際にはコンパイラではありません。

派生オブジェクトが実際に実行時に何であるかをvtablesとvpointersを介して評価するのはC++ランタイムです。

これがどのように行われるかについての良い説明については、ScottMeyerの本EffectiveC++を強くお勧めします。

派生クラスのメソッドのデフォルトパラメータが無視され、基本クラスのデフォルトパラメータが引き続き使用される方法についても説明します。それは拘束力があります。

于 2008-10-14T22:46:31.373 に答える
1

質問の2番目の部分に答えるには、そのアドレスにはおそらく適切な場所にVテーブルがないため、狂気が発生します。また、規格では未定義です。

于 2008-10-14T22:48:45.707 に答える
1
cout << ((Polygon *)0x12345678)->area() << endl;

このコードは、発生するのを待っている災害です。コンパイラはそれを正常にコンパイルしますが、実行時に関しては、有効なvテーブルを指しているわけではなく、運が良ければプログラムがクラッシュするだけです。

C ++では、このような古いCスタイルのキャストを使用しないでください。次のようにdynamic_castを使用する必要があります。

Polygon *obj = dynamic_cast<Polygon *>(0x12345678)->area();
ASSERT(obj != NULL);

cout << obj->area() << endl;

指定されたポインタが有効なポリゴンオブジェクトでない場合、dynamic_castはNULLを返すため、ASSERTによってトラップされます。

于 2008-10-14T23:04:50.603 に答える
1

仮想関数テーブル。つまり、両方のPolygon派生オブジェクトには、すべての(非静的)関数の実装への関数ポインターを含む仮想関数テーブルがあります。Triangleをインスタンス化すると、area()関数の仮想関数ポインターがTriangle :: area()関数を指します。Rectangleをインスタンス化すると、area()関数はRectangle :: area()関数を指します。仮想関数ポインタはオブジェクトのデータとともにメモリに格納されるため、そのオブジェクトをポリゴンとして参照するたびに、そのオブジェクトに適切なarea()が使用されます。

于 2008-10-14T23:06:58.137 に答える