#include <iostream>
#include <stdio.h>
using namespace std;
// Base class
class Shape
{
public:
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
Shape()
{
printf("creating shape \n");
}
Shape(int h,int w)
{
height = h;
width = w;
printf("creatig shape with attributes\n");
}
protected:
int width;
int height;
};
// Derived class
class Rectangle: public Shape
{
public:
int getArea()
{
return (width * height);
}
Rectangle()
{
printf("creating rectangle \n");
}
Rectangle(int h,int w)
{
printf("creating rectangle with attributes \n");
height = h;
width = w;
}
};
int main(void)
{
Rectangle Rect;
Rect.setWidth(5);
Rect.setHeight(7);
Rectangle *square = new Rectangle(5,5);
// Print the area of the object.
cout << "Total area: " << Rect.getArea() << endl;
return 0;
}
プログラムの出力は以下のとおりです
creating shape
creating rectangle
creating shape
creating rectangle with attributes
Total area: 35
両方の派生クラスオブジェクトを構築するとき、最初に呼び出されるのは常にデフォルトで基本クラスコンストラクターであることがわかります。これには理由がありますか?これがpythonのような言語がC++のような暗黙的な呼び出しではなく、基本クラスコンストラクターの明示的な呼び出しを要求する理由ですか?
これに対する短い答えは、「それがC++標準で指定されているからです」です。
次のように、デフォルトとは異なるコンストラクターをいつでも指定できることに注意してください。
class Shape {
Shape() {...} //default constructor
Shape(int h, int w) {....} //some custom constructor
};
class Rectangle : public Shape {
Rectangle(int h, int w) : Shape(h, w) {...} //you can specify which base class constructor to call
}
基本クラスのデフォルトコンストラクターは、呼び出すコンストラクターを指定しない場合にのみ呼び出されます。
派生クラスで別のコンストラクターを明示的に呼び出さない限り、デフォルトのクラスコンストラクターが呼び出されます。言語はこれを指定します。
Rectangle(int h,int w):
Shape(h,w)
{...}
他の基本クラスコンストラクターを呼び出します。
オブジェクトが構築されると、常に最初に基本クラスサブオブジェクトが構築されるため、基本クラスコンストラクターが最初に呼び出され、次に派生クラスコンストラクターが呼び出されます。その理由は、派生クラスオブジェクトには、基本クラスから継承されたサブオブジェクトが含まれているためです。基本クラスのサブオブジェクトを初期化するには、常に基本クラスのコンストラクターを呼び出す必要があります。通常、派生クラスのメンバー初期化リストで基本クラスコンストラクターを呼び出します。基本クラスコンストラクターを明示的に呼び出さない場合、コンパイルは基本クラスの既定のコンストラクターを呼び出して基本クラスサブオブジェクトを初期化します。ただし、デフォルトコンストラクターの暗黙的な呼び出しは常に機能する必要はありません(たとえば、基本クラスが引数なしでは呼び出せないコンストラクターを定義している場合)。
オブジェクトがスコープ外にある場合、最初に派生クラスのデストラクターを呼び出し、次に基本クラスのデストラクターを呼び出します。
C++では、コンパイラは常にオブジェクト階層内の関数が正常に呼び出されるようにします。これらの関数はコンストラクタとデストラクタであり、オブジェクト階層は継承ツリーを意味します。
このルールによれば、コンパイラは、実装していない場合でも、継承階層内の各オブジェクトのコンストラクターとデストラクターを呼び出すと推測できます。この操作を実行するために、コンパイラーは未定義のコンストラクターとデストラクターを合成し、それらをデフォルトのコンストラクターとデストラクターと名付けます。次に、コンパイラーはベースクラスのデフォルトのコンストラクターを呼び出し、派生クラスのコンストラクターを呼び出します。
あなたの場合、ベースクラスコンストラクタを呼び出さないが、コンパイラはベースクラスのデフォルトコンストラクタを呼び出すことでそれを行う。コンパイラがそれを行わなかった場合、あなたの例の長方形である派生クラスは完全ではなく、災害を引き起こす可能性があるためおそらく、派生クラスで基本クラスのメンバー関数を使用するからです。そのため、安全のために、コンパイラは常にすべてのコンストラクター呼び出しを必要とします。
次のように想像してください。サブクラスがスーパークラスからプロパティを継承する場合、魔法のように表示されません。オブジェクトを構築する必要があります。したがって、ベースコンストラクターを呼び出します。クラスが変数を継承し、スーパークラスコンストラクターが重要な値に初期化する場合を想像してください。これを行わなかった場合、変数が初期化されていないため、コードが失敗する可能性があります。
基本クラスのデフォルトコンストラクターが呼び出される理由常にそうであるとは限りません。 (異なるシグネチャを持つ)基本クラスのコンストラクターは、派生クラスのコンストラクターから呼び出すことができます。あなたの場合、パラメータがないためデフォルトのコンストラクタが呼び出され、デフォルトになります。
派生クラスが作成されると、コンストラクターが呼び出される順序は常にBase-> Derived in the hierarchyです。私たちが持っている場合:
class A {..}
class B : A {...}
class C : B {...}
C c;
Cが作成されると、最初にAのコンストラクターが呼び出され、次にBのコンストラクター、次にCのコンストラクターが呼び出されます。
その順序を保証するために、派生クラスのコンストラクターが呼び出されると、派生クラスのコンストラクターが他の操作を行う前に、常に基底クラスのコンストラクターを呼び出します。そのため、プログラマーは、派生クラスのコンストラクターの唯一の初期化リストで、対応するパラメーターを使用して、基本クラスのコンストラクターを手動で呼び出すことができます。たとえば、次のコードでは、Derivedのデフォルトコンストラクターは、デフォルトコンストラクターの代わりにBaseのコンストラクターBase :: Base(int i)を呼び出します。
Derived() : Base(5)
{
}
派生クラスのコンストラクターの初期化リストでそのようなコンストラクターが呼び出されない場合、プログラムはパラメーターのない基本クラスのコンストラクターを想定します。これが、パラメーターのないコンストラクター(つまり、既定のコンストラクター)が呼び出される理由です。