44763


「新明解C++」という参考書で勉強してますが、派生についての記載がないので質問します。

  1. virtualをすると再定義することが出来ると知ったのですが、newした時にデフォルトコンストラクタの挙動はどうなるのでしょうか？
  2. どっちの型でもnew 出来ますがどっちを利用すべきなんでしょか？  
例

[code]     base *b = new base;

    base *bb = new derive;
    
[/code]

  3. また、デスストラクタの挙動はどうなるのでしょうか？

* * *
[code]

    class base {
    public:
        virtual void pri() {
            std::cout << "base\n";
        }
    };
    
    class derive : public base {
    public:
        void pri() {
            std::cout << "virtual\n";
        }
    };
    
    int main() {
    
        base ba;
        derive de;
    
        base *b = new derive;
        base *bb = new base;
        derive *d = new derive;
    
        ba.pri();
        de.pri();
        std::cout << "\n\n";
        b->pri();
        bb->pri();
        d->pri();
    
        _getch();
        return 0;
    }
    
[/code]


CC BY-SA 4.0
2018-06-11T15:40:42.987
44708
2018-06-14T01:46:56.670
2018-06-12T08:39:38.810
question
c++
c++　継承クラスを理解したい


ここはオブジェクト指向の教科書を読むべきだと思いますが

  * 基底クラスは抽象的な「性質」を表現する
  * 派生クラスはその性質を「具体的に」実装する

と認識していただければ、まず間違っていないと思います。このことさえ抑えておけば「コンストラクタがどうこう」「 `new`
がどうこう」というのは枝葉末節だとそのうち理解できるでしょう。

例：

Windows の画面上に現れる「部品」があって、その部品とは  
\- マウスカーソルが自分の上にある場合に何かする  
\- 左ボタンが押されたら何かする  
\- 右ボタンが押されたら何かする  
などなどの共通な「性質」があり、それは基底クラスで表現します。

一方で  
\- マウスカーソルが自分の上にある場合に具体的に何をする  
\- 左ボタンが押されたら具体的に何をする  
\- 右ボタンが押されたら具体的に何をする  
あたりは「実装」。部品の種類ごとに皆違うはずで、それは派生クラスで表現します。

マウスの処理関数は「左ボタンが押された」ことを認識して基底クラスのメンバ関数を呼ぶ。  
呼び出し元の呼び方に関係なく、実際に呼び出されるのは派生クラスのメンバ関数である。  
あたりが理解できるとこの先の理解が一気に進むと思います。  
# その部品をどう構築したかは、今理解すべき本筋とは無関係なので考える必要はない

  * 我々末端プログラマが組むのは派生クラス（における具体的処理の実装）
  * Windows 自体やクラスライブラリが呼ぶのは基底クラスにあるメンバ関数（クラスライブラリの設計段階では派生クラスはまだ実装されていない、実装するのはクラスライブラリのユーザーすなわち我々なのだから）
  * ソースコードの字面上、クラスライブラリが基底クラスのメンバ関数を呼んでいても、実際に呼ばれるのは派生クラスのメンバ関数、つまり我々が実装した関数
  * その辺を自動的に工夫してくれるのが継承・派生関係とか `virtual` とか

ってことで。


CC BY-SA 4.0
2018-06-12T00:28:43.533
44710
2018-06-12T00:28:43.533
8589
44708
answer


元質問が編集されて内容が変わっている上、改善待ちも消えたので別回答を追加

正直、適切な教科書を読めば必ず載っている内容で目新しいものはありません。良い教科書を探して購入されることをお勧めします。オイラは持っていませんがそのシリーズの「中級編」でもよいのかもしれません。まあ
SO で訊くのも大いにアリですけどね。

A1. コンストラクタの動作には何一つ変化ありません。  
デフォルトコンストラクタだろうが非自明コンストラクタだろうが、コンストラクタ関数の呼び出される順序は  
基底クラスのコンストラクタ→自クラスのメンバのコンストラクタ→自クラスのコンストラクタの自作記述部（を再帰的にたどる）

`virtual` でコンストラクタを上書きや再定義することはできません（意味ないため）

A3.
継承・派生機構を使って、派生クラスのインスタンスを基底クラスへのポインタないしは参照で保持している場合に話を限定します（そうでない＝派生クラスのインスタンスを当該クラスへのポインタで扱う場合には下記考慮は不要）

そういう場合 [c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示")
では適切なデストラクタを起動するためには一工夫が必要です（他の言語ではこの手の工夫をしなくてよいものもあります)。

工夫とはデストラクタに `virtual` をつけるだけです。逆に言うと「デストラクタに `virtual`
がついていないクラスは、その作者がこのクラスを派生させるなと意思表明している」と読むことができます。

サンプル：

[code]

    struct a     { ~a() { std::cout << "a::~a()\n"; } };
    struct b : a { ~b() { std::cout << "b::~b()\n"; } };
    struct c : b { ~c() { std::cout << "c::~c()\n"; } };
    
[/code]

たとえば

[code]

    int main() { b obj; }
    
[/code]

の挙動と

[code]

    int main() { a* p=new b(); delete p; }
    
[/code]

の挙動を比較してみてください。そののち、上記例を `virtual ~a() { ... }` に修正して更に試してみるとよいかもしれません。

A2. まったく違います。で、両方とも間違っているんだと思います。

別回答で書いたとおり、クラスライブラリが提供している基底クラスに対してオイラたちクラスライブラリのユーザーが派生クラスを作る＝抽象的な何かに具体的機能を追加する、ってことです。オイラたちはその必要があるから具体的機能を追加したわけで、当然自分の書いているプログラムの中からはその具体的機能を使いたいってことです。以下そういう状況に話を限定。

`base* p=new base();` の意味は機能追加前のクラスを使うってことなので、ほぼまったく無意味（クラスライブラリを派生させた意味がない）

`base* p=new derived();` の意味は機能を追加したクラスのインスタンスを作るが、その追加機能を使うすべが無いってことでこれもほぼ無意味

`derived* p=new derived();` であれば、追加機能があるインスタンスを作って、その追加機能を `p`
経由で呼ぶことができる、ってことなので意味があるわけです。この状況なら、クラスライブラリ側が用意している関数 `extern int
DoModal(base*);` を `DoModal(p)` のように呼び出すことができます（説明のために非メンバ関数としておく）。ここで
`derived*` → `base*` の暗黙変換がなされています。

原則論として、派生→基底のポインタ変換は作者が違う「境界層」で使うこととなります。数ヶ月前の自分は他人ですからそういう場合にも使うことがあるでしょうね。

SO/SE 的には発言１スレッドに付き質問１回答１が望ましいとされているようなので、次からはぜひそうしてください。


CC BY-SA 4.0
2018-06-14T01:46:56.670
44763
2018-06-14T01:46:56.670
8589
44708
answer