【Effective C++】継承されたデフォルトパラメータ値を再定義しない

7996 ワード

このような継承システムを考えてみましょう.
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はダイナミックバインドであり、デフォルトのパラメータ値は静的バインドであるためです.
静的バインドと動的バインド
オブジェクトのタイプは、静的タイプと動的タイプに分けられます.もちろん、ここでのオブジェクトは、参照を含むポインタタイプのオブジェクトにのみ適用されます.
  • スタティックタイプ:オブジェクトがプログラムで宣言されたタイプ
  • ダイナミックタイプ:オブジェクトが現在実際に指向しているタイプ
  • virtual関数は動的にバインドされています.すなわち、virtual関数を呼び出すと、継承システムのどのコードを呼び出すかは、呼び出されたオブジェクトの動的タイプに依存します.
    デフォルトパラメータは静的にバインドされます.つまり、関数のデフォルトパラメータの関数を呼び出すと、呼び出されたオブジェクトの静的タイプが呼び出されます.
    したがって、上記のpr->draw();については、draw()がvirtual functionであるため、呼び出し対象prを発行する動的タイプRectangledraw()関数、すなわち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;
    };