9120


ストラウストラップのプログラミング入門の第１４章の練習問題１がわかりません。  
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]

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


CC BY-SA 3.0
2015-04-13T10:36:08.337
9115
2015-04-14T07:49:41.520
9299
question
c++
派生クラスのコンストラクタ


すみません。自分で解決できました。  
Circleのクラスに次のコードを追加しました。

[code]

    Circle() { }
    
[/code]


CC BY-SA 3.0
2015-04-13T11:33:37.527
9116
2015-04-13T11:33:37.527
9299
9115
answer


`Smiley(Graph_lib::Point)` というコンストラクタで `Circle()` という関数を呼び出そうとしているが見つからない。
`Circle(Point p, int rr)` がそれに近いが、引数が2つ必要なのに対して一つも渡されていない。・・・という内容のエラーですね。

構造体やクラスを定義する際には自動的に引数なしのコンストラクタが定義されますが、自分でコンストラクタを定義した場合はこれが行われません。ですから、

  * Circle クラスに引数なしのコンストラクタを追加する
  * Smiley のコンストラクタで、適切な引数を指定して `Circle(Point p, int rr)` を呼び出す

のどちらかになると思います。

後者の場合、基底クラスの名前を使ってこういう形になるかと。

[code]

    Smiley::Smiley(Point p)
        : Circle(p, 100)
        , 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) { }
    
[/code]


CC BY-SA 3.0
2015-04-13T11:43:49.233
9117
2015-04-13T12:36:49.667
2015-04-13T12:36:49.667
8000
8000
9115
answer


そもそも `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]


CC BY-SA 3.0
2015-04-13T12:55:21.700
9120
2015-04-13T12:55:21.700
5300
9115
answer


解決はしているようですが、今回のクラス設計を見ると、いわゆる is-a 関係と has-a 関係をうまく意識できていないのかな、と思ったので補足。

is-a 関係: ○○は××の一種である、とする関係。継承で表現できる。  
has-a 関係: ○○は××を構成要素のひとつとして持つ、とする関係。こちらは継承ではなくメンバーを持つという形で表現する。

で、今回はどっちが適切かと言うと、基本は has-a かな。  
とはいえ、課題で `Circle` の派生クラスとすることを求められているというなら is-a となるように考えなければなりません。この辺は考え方ひとつ。  
以下に考え方の例をいくつか挙げてみます (間違った考え方も混ざっているので注意)。

## 考え方1: (ダメ発想) スマイリーは3つの円によって顔を構成されるので円の一種

この考え方には無理があります。別の例を挙げてみると、三色旗は3つの色によって構成されるから色の一種だ、のような。  
has-a と is-a を混同するとこういった発想になってしまうことがありますが、まず間違いだと言えます。

## 考え方2: スマイリーは3つの円によって顔を構成されるもの

この場合、スマイリークラスが3つの円を持つ、と考えるので has-a 関係となります。  
`Smiley` は `Circle` を継承するのではなく、単純にメンバーとして持ちます。

[code]

    class Smiley { // Circleを継承しない
    private:
        Circle face;
        Circle reye;
        Circle leye;
        ...
    }
    
[/code]

## 考え方3: スマイリーは円のシルエットを持つから円の一種

課題の解答としてはこの考え方かな。  
この場合は、 is-a 関係と has-a 関係の合わせ技になります。  
輪郭部分を以て円の一種とするので、輪郭に関しては is-a 関係になります。  
顔パーツの `Circle` については、「スマイリーは円の一種」と言うところの「円」とは無関係の顔の部品であるため、`Smiley` とは has-a
関係になります。

[code]

    class Smiley : public Circle { // face は基底クラスで表現
    private:
        Circle reye;
        Circle leye;
        ...
    }
    
[/code]


CC BY-SA 3.0
2015-04-14T04:28:29.373
9139
2015-04-14T07:49:41.520
2015-04-14T07:49:41.520
7831
7831
9115
answer