【Effective C++】継承されたデフォルトパラメータ値を再定義しない
このような継承システムを考えてみましょう.
デフォルトでは、
次の状況を考慮します.
PC->draw()で描かれた円形は赤で、pr->draw()で描かれた矩形は赤です.これは、virtual functionはダイナミックバインドであり、デフォルトのパラメータ値は静的バインドであるためです.
静的バインドと動的バインド
オブジェクトのタイプは、静的タイプと動的タイプに分けられます.もちろん、ここでのオブジェクトは、参照を含むポインタタイプのオブジェクトにのみ適用されます.スタティックタイプ:オブジェクトがプログラムで宣言されたタイプ ダイナミックタイプ:オブジェクトが現在実際に指向しているタイプ virtual関数は動的にバインドされています.すなわち、virtual関数を呼び出すと、継承システムのどのコードを呼び出すかは、呼び出されたオブジェクトの動的タイプに依存します.
デフォルトパラメータは静的にバインドされます.つまり、関数のデフォルトパラメータの関数を呼び出すと、呼び出されたオブジェクトの静的タイプが呼び出されます.
したがって、上記の
したがって、継承されたデフォルトのパラメータ値を再定義する必要はありません.そうしないと、予期せぬ動作を引き起こす可能性があります.
NVIはvirtual functionを実現する
上記のシナリオでは、NVIを使用してvirtual functionを実装し、サブクラスを配置してデフォルトパラメータを書き換える必要があります.NVIについては、この記事を参考にすることができます.
class Shape
{
public:
enum ShapeColor {Red, Green, Blue};
virtual void draw(ShapeColor color = Red) const = 0;
//...
};
class Rectangle : public Shape
{
public:
virtual void draw(ShapeColor color = Green) const;
};
class Circle : public Shape
{
public:
virtual void draw(ShapeColor color) const;
//...
};
デフォルトでは、
Shape
から継承されたタイプは赤です.しかし同時に、Rectangle
クラスでこのデフォルトの色を再定義し、Rectangle
のデフォルトの色が緑であることを望んでいます.しかし、事実は本当にそうですか?次の状況を考慮します.
Shape *ps;
Shape *pc = new Circle();
Shape *pr = new Rectangle();
pc->draw();
pr->draw();
PC->draw()で描かれた円形は赤で、pr->draw()で描かれた矩形は赤です.これは、virtual functionはダイナミックバインドであり、デフォルトのパラメータ値は静的バインドであるためです.
静的バインドと動的バインド
オブジェクトのタイプは、静的タイプと動的タイプに分けられます.もちろん、ここでのオブジェクトは、参照を含むポインタタイプのオブジェクトにのみ適用されます.
デフォルトパラメータは静的にバインドされます.つまり、関数のデフォルトパラメータの関数を呼び出すと、呼び出されたオブジェクトの静的タイプが呼び出されます.
したがって、上記の
pr->draw();
については、draw()
がvirtual functionであるため、呼び出し対象pr
を発行する動的タイプRectangle
のdraw()
関数、すなわちRectangle::draw();
を呼び出すが、デフォルトパラメータについてはpr
の静的タイプがShape
であるため、Shape::draw()
のデフォルトパラメータRedが得られる.したがって、継承されたデフォルトのパラメータ値を再定義する必要はありません.そうしないと、予期せぬ動作を引き起こす可能性があります.
NVIはvirtual functionを実現する
上記のシナリオでは、NVIを使用してvirtual functionを実装し、サブクラスを配置してデフォルトパラメータを書き換える必要があります.NVIについては、この記事を参考にすることができます.
class Shape
{
public:
enum ShapeColor {Red, Green, Blue};
void draw(ShapeColor color = Red) const
{
doDraw(color);
}
//...
private:
virtual void doDraw(ShaperColor color) const;
//...
};
class Rectangle : public Shape
{
public:
//...
private:
virtual void doDraw(ShaperColor color) const;
};
class Circle : public Shape
{
public:
//...
private:
virtual void doDraw(ShaperColor color) const;
};