5

VTableについてたくさん読んだ後でも、未回答の質問が1つあります。

次のクラスを考えると:

#include <iostream>
using namespace std;

class Shape {
public:
    int* a;
    Shape(){
        cout<<"default Shape ctor"<<endl;
        a = new int(15); // default
    }
    Shape(int n){
        a = new int(n);
          cout<<"Shape(n) constructor"<<endl;
    }
    // copy constructor
    Shape(const Shape& s){
        cout<<"copy constructor"<<endl;
        a = new int(*(s.a));
    }
    Shape& operator=(const Shape& s){
        cout<<"operator="<<endl;

        if (&s == (this))
            return (*this);
//      this.clear();
        a = new int(*(s.a));

        return (*this);
    }


      virtual void draw(){
             cout<<"print Shape the number is "<<*a<<endl;
      };
      virtual ~Shape(){
          delete a;
          cout<<"Shape distructor"<<endl;
      }
};

class Circle : public Shape {
public:
    int b;
  Circle() {
      cout<<"Circle constructor"<<endl;
      b=5;
  }
  virtual void draw() {
      cout<<"print Circle. The number is "<<b<<endl;
  }
   ~Circle(){
      cout<<"Circle distructor"<<endl;
    }
};

および次のテスト:

static void test2(){
    Circle* c = new Circle();
    cout<<"size of *c is "<<sizeof(*c)<<endl;
    Shape* s = c;
    cout<<"size of *s is "<<sizeof(*s)<<endl;
    s->draw();
}

私はこの出力を取得します:

default Shape ctor
Circle constructor
size of *c is 12
size of *s is 8
print Circle. The number is 5

私の質問は次のとおりです。sがCircle::drawをどのようにアドレス指定するかは知っていますが、変数b = 5をどのように知ることができますか?このテストが示すように、sにはこの情報がありません。ここで何が欠けていますか?

ありがとう!

OKみんな。迅速な回答をありがとう...

あなたの回答から、Circle :: draw()(* this)はCircleタイプであることがわかりました。わかった。私の質問はこれに変わりました。私はsをShape*タイプにしたいだけだったので、つまり、プログラムではShape品質だけが必要でした。次の4バイト(Circleのb変数)がコンパイラによって何らかの形で取得される可能性はありますか?その場合、明らかにCircle :: draw()は期待どおりに機能しません。

そうでない場合、コンパイラは、sの「終了」の後にこれらの次の4バイトが必要であることをどのように認識しますか?

4

4 に答える 4

2

欠落しているのは、 -をs指し、にと呼ばれるデータメンバーが含まれていることです。が呼び出されると、コンパイラは、あなたが認識しているように、を呼び出します。その中で、のタイプ(つまり現在のオブジェクト)はではありません。したがって、にアクセスできます。CircleCirclebs->draw();Circle::draw()Circle::draw()*thisCircleShapeCircle::draw()b

編集:あなたの新しい質問への答えsは、-へのポインタですShape-あなたがしているのは、同じアドレス(Circleメモリ内のオブジェクトの先頭へ)を(Shape*の代わりにCircle*)異なるタイプで格納することだけです。基になるCircleオブジェクトは、それを指しているものに関係なく、メモリ内に存在します。Circleを介して特定のデータメンバーにs直接アクセスすることはできませんShape*が、仮想ディスパッチメカニズムは、を介して仮想メンバー関数をs呼び出すと、呼び出しがの適切なメンバー関数に転送されることを意味します。Circleつまり、s->draw();実際にはを呼び出しCircle::drawます。Circle基になるオブジェクトのアドレスをに格納した結果として、Shape*、基になるCircleオブジェクトは何らかの形で「スライス」され、bデータメンバーが削除されます。スライスは、次のような場合にのみ発生します。

Circle c;
Shape s = c; // copies the Shape data members across from c, but slices off the rest
于 2011-04-01T10:39:53.953 に答える
1
  1. sizeofコンパイル時のものになる傾向があります。s;が指すオブジェクトを見ていません。sそれはsを指しているのを見て、Shapeあなたに。のサイズを与えているだけですShape。情報はまだそこにあります。sコンパイラは、を指す事実を追跡していないため、それを表示していませんCircle。ここで適切なサイズを取得するには、キャスト*sバックする必要がありますが、それは、意図した目的を破ると想像する、Circleと言うのと同じことです。sizeof(Circle)

  2. sShapeを指していることと、メソッドを呼び出すShape方法以外は何も知りません。drawは上のメソッドなのでShape、呼び出すことができますが、仮想メソッドであるため、オブジェクトには「for、callhere」のようなルックアップテーブルがありますdraw()。の場合Circle*、そのテーブルは--を指してCircle::drawいるため、サブクラスのメソッドが呼び出されます。そして、ポインタは実際にはを指しているので、残りのフィールドCircleの後にaがあります(それだけとそのサブクラスが存在することさえ知っています)。bShapeCircle

于 2011-04-01T10:39:53.647 に答える
0

あなたが言うように、sはCircleのインスタンスのdrawメソッドだけでなく、Circleのインスタンスを指します。したがって、CircleのメソッドはCircleのインスタンス変数にアクセスできます。したがって、Circle :: drawが呼び出されると、Circleのメンバーであるため、インスタンス変数を「見る」ことができます。

于 2011-04-01T10:41:22.917 に答える
0

sizeof()渡されたパラメータのタイプのサイズを示します。この場合、のタイプ*sShapeクラスです。しかし、その値がのメンバーでもあるオーバーライドされたメソッドに自然にアクセスできるクラスsのインスタンスを指しています。質問のタイトルの一部として、これはポリモーフィズムであり、ポリモーフィズムとは何の関係もありません。Circledraw()bCirclesizeof()

これは関数にも当てはまりtypeid()、パラメーターの値やオブジェクトではなく、パラメーターのタイプで機能します。パラメーターが指します。

于 2011-04-01T11:27:04.463 に答える