派生クラスのコンストラクタ


ストラウストラップのプログラミング入門の第１４章の練習問題１がわかりません。  
C++です。派生クラスのコンストラクタが上手くいっていないようです。  
初めて質問するので、書き方など拙かったらすみません。

Circleという基底クラスとSmileyという派生クラスがあります。  
SmileyにはCircleを三つ入れて顔を描画するつもりです。  
（最終的にはark_ellipseという同じShapeが基底のクラスのオブジェクトもSmileyにいれたいと思っています。）  
ちなみにCircleにはさらにShapeという基底クラスがあります。  
Shapeは抽象クラスでそれぞれの派生クラスでdraw_lines()関数をオーバーライドして図形を描画するプログラムです。  
ちなみにfltkというグラフィックライブラリを使用しています。  
全部のコードをのせると長いので抜粋します。

これがCircleクラスです。

[code]

    struct Circle : Shape {
        Circle(Point p, int rr) // center and radius
        :r{ rr } {
            add(Point{ p.x - r, p.y - r });
        }
    
        void draw_lines() const;
    
        Point center() const { return { point(0).x + r, point(0).y + r }; }
    
        void set_radius(int rr) { r=rr; }
        int radius() const { return r; }
    private:
        int r;
    };
    
[/code]

一応draw_lines()ものせます。

[code]

    void Circle::draw_lines() const
    {
        if (fill_color().visibility()) {    // fill
            fl_color(fill_color().as_int());
            fl_pie(point(0).x,point(0).y,r+r-1,r+r-1,0,360);
            fl_color(color().as_int()); // reset color
        }
    
        if (color().visibility()) {
            fl_color(color().as_int());
            fl_arc(point(0).x,point(0).y,r+r,r+r,0,360);
        }
    }
    
[/code]

これがSmileyクラスです。

[code]

    struct Smiley : Circle {
        Smiley(Point p);
    
        Circle reye;
        Circle leye;
        Circle face;
    
        void draw_lines() const;
    };
    
    Smiley::Smiley(Point p)
        :reye(Point(p.x+100,p.y+100),50), leye(Point(p.x+300,p.y+100),50), face(Point(p.x,p.y),400) { }
    
    void Smiley::draw_lines() const
    {
        reye.draw_lines();
        leye.draw_lines();
        face.draw_lines();
    }
    
[/code]

ちなみに各クラスが使用しているPointというクラスはこんな感じでwindowのx座標とy座標を指しています。

[code]

    struct Point {
        int x,y;
        Point(int xx, int yy) : x(xx), y(yy) { }
        Point() :x(0), y(0) { }
    };
    
[/code]

これを実行すると、

[code]

    ch14_e1.cpp: In constructor ‘Graph_lib::Smiley::Smiley(Graph_lib::Point)’:
    ch14_e1.cpp:67:95: error: no matching function for call to    ‘Graph_lib::Circle::Circle()’
    :reye(Point(p.x+100,p.y+100),50), leye(Point(p.x+300,p.y+100),50), face(Point(p.x,p.y),400) { }
                                                                                                 ^
    ch14_e1.cpp:67:95: note: candidate is:
    In file included from GUI.h:13:0,
                 from Simple_window.h:10,
                 from ch14_e1.cpp:2:
    Graph.h:282:2: note: Graph_lib::Circle::Circle(Graph_lib::Point, int)
    Circle(Point p, int rr) // center and radius
    ^
    Graph.h:282:2: note:   candidate expects 2 arguments, 0 provided
    
[/code]

という感じのコンパイルエラーが出ます。  
大変長くなってしまいましたが、宜しくお願いします。




そもそも `Smiley` が 3 つの円で構成されるのに、4 つも `Circle` を使っているのが誤りです。

[code]

    struct Smiley : Circle { // ← 1つ目 (??)
        Smiley(Point p);
    
        Circle reye; // ← 2つ目 (右の目)
        Circle leye; // ← 3つ目 (左の目)
        Circle face; // ← 4つ目 (輪郭)
    
        void draw_lines() const;
    };
    
[/code]

確かに `Circle(p, 100)` の様にして、基底クラスに対してダミーの初期化を指定すれば _コンパイルが通る様にはなります_
が、仰る通り「意味不明なコード」です。正しく意味のあるコードにする為には:

  * `Smiley` を `Circle` から派生させる事が要件であれば、例えば以下の様になるでしょう。
[code]     struct Smiley : Circle { // ← 1つ目 (輪郭)

        Smiley(const Point& p);
        Circle reye; // ← 2つ目 (右の目)
        Circle leye; // ← 3つ目 (左の目)
        void draw_lines() const;
    };
    
    Smiley::Smiley(const Point& p)
      :Circle(p,400),
       reye(Point(p.x+100,p.y+100),50),
       leye(Point(p.x+300,p.y+100),50) { }
    
    void Smiley::draw_lines() const
    {
        this->reye.draw_lines(); // 左目
        this->leye.draw_lines(); // 右目
        this->Circle::draw_lines(); // 基底クラス (輪郭)
    }
    
[/code]

  * 書き換えを最小にするならば、以下のようになるでしょう。
[code]     struct Smiley : Shape { // ← 単に Shape にする

        Smiley(const Point& p);
        Circle reye; // ← 1つ目 (右の目)
        Circle leye; // ← 2つ目 (左の目)
        Circle face; // ← 3つ目 (輪郭)
        void draw_lines() const;
    };
    
    Smiley::Smiley(const Point& p)
      :reye(Point(p.x+100,p.y+100),50),
       leye(Point(p.x+300,p.y+100),50),
       face(p,400) { }
    
    // void draw_lines() const; はそのまま
    
[/code]




そもそも `Smiley` が 3 つの円で構成されるのに、4 つも `Circle` を使っているのが誤りです。

[code]

    struct Smiley : Circle { // ← 1つ目 (??)
        Smiley(Point p);
    
        Circle reye; // ← 2つ目 (右の目)
        Circle leye; // ← 3つ目 (左の目)
        Circle face; // ← 4つ目 (輪郭)
    
        void draw_lines() const;
    };
    
[/code]

確かに `Circle(p, 100)` の様にして、基底クラスに対してダミーの初期化を指定すれば _コンパイルが通る様にはなります_
が、仰る通り「意味不明なコード」です。正しく意味のあるコードにする為には:

  * `Smiley` を `Circle` から派生させる事が要件であれば、例えば以下の様になるでしょう。
[code]     struct Smiley : Circle { // ← 1つ目 (輪郭)

        Smiley(const Point& p);
        Circle reye; // ← 2つ目 (右の目)
        Circle leye; // ← 3つ目 (左の目)
        void draw_lines() const;
    };
    
    Smiley::Smiley(const Point& p)
      :Circle(p,400),
       reye(Point(p.x+100,p.y+100),50),
       leye(Point(p.x+300,p.y+100),50) { }
    
    void Smiley::draw_lines() const
    {
        this->reye.draw_lines(); // 左目
        this->leye.draw_lines(); // 右目
        this->Circle::draw_lines(); // 基底クラス (輪郭)
    }
    
[/code]

  * 書き換えを最小にするならば、以下のようになるでしょう。
[code]     struct Smiley : Shape { // ← 単に Shape にする

        Smiley(const Point& p);
        Circle reye; // ← 1つ目 (右の目)
        Circle leye; // ← 2つ目 (左の目)
        Circle face; // ← 3つ目 (輪郭)
        void draw_lines() const;
    };
    
    Smiley::Smiley(const Point& p)
      :reye(Point(p.x+100,p.y+100),50),
       leye(Point(p.x+300,p.y+100),50),
       face(p,400) { }
    
    // void draw_lines() const; はそのまま
    
[/code]

