ここで何が起こっているのか理解できませんでした。非常に奇妙だと思いました。その理由を理解した後、答えを共有することは誰かの時間にとって価値があると思いました。
したがって、次の単純なコードが与えられます。
#include <iostream>
using namespace std;
class Shape {
public:
int* a;
Shape(){
cout<<"Default Shape constructor"<<endl;
a = new int(8); // default
}
Shape(int n){
a = new int(n);
cout<<"Shape(n) constructor"<<endl;
}
// copy constructor
Shape(const Shape& s){
cout<<"Shape copy constructor"<<endl;
a = new int(*(s.a));
}
Shape& operator=(const Shape& s){
cout<<"Shape 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<<"Default Circle constructor"<<endl;
b=0;
}
virtual void draw() {
cout<<"Printing Circle. The number is "<<b<<endl;
}
~Circle(){
cout<<"Circle distructor"<<endl;
}
};
次の 2 つのテストで 2 つの異なる答えが得られるのはなぜですか。
static void test1(){
Shape shape = Circle() ;
shape.draw();
}
static void test2(){
Shape* shape = new Circle() ;
shape->draw();
delete shape;
}
ま、仮想の仕組みはついつい知ってしまったので、どちらのテストでも同じ結果が得られると思いました(Circleを印刷)。これはtest2で発生することですが、 test1ではそうではありません。
その理由を理解するために、バックグラウンドで実際に何が起こっているかを書きました。
Test1: 1. プログラムは「Circle()」という行を実行します。1.1 Shape のデフォルト コンストラクタが呼び出されます (Circle は Shape から派生しているため)。1.2 Circle のデフォルト コンストラクタが呼び出されます。
- プログラムはアクション「Shape shape =」を実行します。これは実際に Shape のコピー コンストラクターを呼び出します。*ここで、コピー コンストラクターは Circle の非表示フィールドである _vptr をコピーしないことに注意してください。a の値をコピーして (*this) を返すだけです。これが Circle を印刷しない本当の理由です。
ここで、別の質問があります。test1 を実行すると、次の出力が得 られました。
コピー コンストラクターのシグネチャがShape(const Shape& s) の場合、この出力によると、シェイプを実際にShapeとして作成する前に、コピー コンストラクターの呼び出しがあります。これはどのように起こりますか?
Test2: 1. クラス Circle の新しいインスタンスがヒープ上に構築されています。(行new Circleが実行されます) 2. ヒープ上のメモリ内のそのアドレスへのポインターが返され、ポインターの形状に配置されます。このアドレスの最初の 4 バイトには、Circle の仮想テーブルへのポインターがあります。これが、test1 が test2 と異なる理由です。
テストの違いは、test1 がスタック上に Circle を構築し、test2 がヒープ上に Circle を構築するという事実とは何の関係もないことを理解することが重要です。いや、実は関係あるんです。しかし、本当の理由は、コピー コンストラクターが _vptr をコピーしないことです。