質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

3952閲覧

クラス階層と多重継承

strike1217

総合スコア651

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

1クリップ

投稿2018/09/21 03:10

クラス階層と多重継承についてです。
参考書籍は、プログラミング言語C++ 第4版 です。
(予定していたvirtualに関する質問は自力で解決できました。)

,基本クラスのポインタに派生クラスのオブジェクトのアドレスを代入する時、継承関係は、public継承でなくてはならない理由は何でしょうか??

C++

1class Employee(){ } 2class manager : public Employee {} 3 4manager obj; 5Employee * ptr = &obj; // ok

自分の予想です。
public継承以外だと、あるメソッドやメンバ変数のアクセス指定子が基本クラスと派生クラスとで異なるものになってしまうから
でしょうか?(たぶん)

,virtual継承の効果について
virtual継承がよく分かりません。

要は、virtual継承と言うのは、多重継承の際に使用する物!
ということですよね。
多重継承の際に複数の基本クラスを唯一のオブジェクトにする これだけですか??
virtualと付いていると仮想関数と混同しちゃうんですが・・・これは、仮想関数とは全く別物という理解で正しいのでしょうか?

virtual継承の場合も実行時型情報は付与されるのでしょうか?
virtual継承を行った場合、その基本クラスはポリモーフィック型になるんでしょうか?それとも派生クラスの方かな?

以下書籍の中で重要そうなことの抜粋

・仮想基底クラスを表現する共有オブジェクトを指すポインタはオフセットになる。

・多くの場合、1個の仮想基底ごとに1ワードのメモリのオーバーヘッドとなる。(64bitCPUだと、8btyeですかね?)
・仮想基底は、最派生クラスにとって、直接の基底とみなせるのである。
・仮想基底のデストラクタは、ちょうど1回だけ実行される。
・仮想基底関数のオーバーライドの際に、異なる複数の派生クラスが、同じ関数をオーバーライドするとどうなるだろうか?それが認められるのは、オーバーライドしようとするクラスが、その関数をオーバーライドする別の全てのクラスから派生している場合に限られる。すなわち、1個の関数で、他の全てをオーバーライドしなければならない。
2個のクラスが基底クラスの1個の関数をオーバーライドしていながら、それらのクラスの一方が、もう一方をお互いにオーバーライドしていなければ、そのクラス階層はエラーとなる。

3つ目は、えーーと。どんなに深くクラス階層を作ったとしてもvirutal継承をしていれば、基底クラスはすべて直接の基本クラスとなる。ということですよね。つまり、間接の基本クラスになる場合はない。

4つ目は、えーーーー。これはなんのことだかよく分かりませんね。
virtual継承の際には、仮想基底クラスのすべての関数を1つの各派生クラスでオーバーライドしなくてはならない。ということですかね。

イマイチvirtual継承がよく分からない。

,クロスキャストって必要なものなのでしょうか??

「ダウンキャスト」の場合、ゲームなどでは時々登場する

と言った内容の記事を見たことがあります。
じゃぁ、クロスキャストは??どんな時に便利で、どんな時に必要になるんでしょうか?

第56章 クロスキャスト

簡単にいうと、クロスキャストはダウンキャストしてアップキャストすることです。ダウンキャストを伴う以上危険性はありますが、これはダウンキャストと同じ方法で回避することが出来ます。第2部第42章に話した dynamic_cast ですね。ランタイムタイプ情報(RTTI)を有効にするのも忘れてはいけません。

2段階のキャストが暗黙の内に行われているようですね。
何に使うのか全く分からない。

環境は Linux g++ です。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

こんにちは。

基本クラスのポインタに派生クラスのオブジェクトのアドレスを代入する時

これはpublic継承なら暗黙の型変換により可能です。
しかし、非public継承の場合、基底クラスは外部から見えない建前になります。なので、暗黙の型変換できるのはおかしいと思います。非publicなキャスト演算子が定義されている場合と同様に考えて良いと思います。

要は、virtual継承と言うのは、多重継承の際に使用する物!

う~ん、たぶんちょっと違います。多重継承の中でも特殊なダイヤモンド継承したい時に使うものと理解しています。

virtual継承の場合も実行時型情報は付与されるのでしょうか?

これはYESの筈です。実行時型情報を付与しない理由はないです。

virtual継承を行った場合、その基本クラスはポリモーフィック型になるんでしょうか?それとも派生クラスの方かな?

派生クラス側です。派生クラスにとって、そのvirtual基底クラスがどこにあるのかコンストラクト時に決定されます。(virtualでなければコンパイル時に決定できます。)
なので、そのポインタを vtable に記録している筈です。(ちょっと自信なし)

・仮想基底は、最派生クラスにとって、直接の基底とみなせるのである。

たしかによく分からんです。直接の基底、間接の基底のどの辺がvirtualの場合と非virtualの場合で異なるのか具体的な記述が必要ですね。

・仮想基底関数のオーバーライドの際に、(後略)

ややこしい話ですね。ダイヤモンド継承したBとCがAのfoo関数をオーバーライドした場合、Dがfooを呼び出した時何が起こるのでしょうか?って話ですね。ワケワカメになるので「それは禁止」ってことかも。
(せっかくvirtual継承して、B::とかC::を省略できるようにしたのに、BかCがオーバーライドしたらB::、C::を省略できなくなります。バカですよね。)
virtual基底クラスを直列に継承した時のみ、オーバーライドしても良いってことのようです。あり得ないわけではないでしょうが、相当特殊なケースでしか使わない気がします。それをちゃんと設計して使いこなせる人は「神」かも。

クロスキャストって必要なものなのでしょうか??

必要になったら思い出せば十分な話のように思います。
FooとBarを多重継承したBazがあり、Foo* foo=new Baz;Bar*へキャストすることがクロスキャストのようです。
必要になることは結構レアと思います。

投稿2018/09/21 03:53

編集2018/09/21 03:54
Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2018/09/21 04:16

> public継承なら暗黙の型変換により可能です。 これって、非public継承をしている場合、明示的なキャストで強制的に代入することって可能でしたっけ? (あ、Cスタイルのキャストだと可能だったかな?) > 派生クラス側です。 なるほど! 派生クラスがポリモーフィック型で、実行時型情報は派生クラスが持っている。ということですね。 継承すると、基本的に派生クラスは基本クラスのメンバ関数を持っています。 もしかして、virtual継承すると派生クラス内では、基本クラス内の全メンバ関数はvirtual関数になる・・・とかですかね? それは、ありえないですよね?
Chironian

2018/09/21 04:24

privateなキャスト演算子が有るとき、暗黙の型変換されませんね。 明示的な型変換もやっぱり無理と思います。privateなので呼び出せませんから。 > 基本クラス内の全メンバ関数はvirtual関数になる それはないです。 単にvirtual基底クラスへのポインタがvtableに登録されるだけです。 基底クラス自体の定義には一切触りませんから、基底クラスの外観も一切変わりません。
strike1217

2018/09/21 04:36 編集

> それはないです。 あ、やはりそれはないんですね。 そうか!多重継承の際に、vtableへのポインタを各派生クラスで共有すれば、「多重継承の際に複数の基本クラスを唯一のオブジェクトにする」ことが可能なわけですね。 > 直接の基底、間接の基底のどの辺がvirtualの場合と非virtualの場合で異なるのか具体的な記述が必要ですね。 それは、自分も思いました。(私の書籍の中には特に書いてないですね。) > せっかくvirtual継承して、B::とかC::を省略できるようにしたのに、BかCがオーバーライドしたらB::、C::を省略できなくなります。 あ!なるほど!その説明は分かりやすいですね。
strike1217

2018/09/21 05:18

多重継承ではなく、直列に継承した場合、virtual継承とするメリットって何かあるんですかね? 私の持っている本にはこうあります。 > この「唯一オブジェクト」という働き以外は、仮想基底クラスと通常の基底クラスに機能の違いはありません。 つまり、直列に継承を行ってもvirutal継承と通常の継承に特に違いはない ということになりそうですが・・ ということは、多重継承の時のみに使うメリットがあるということですよね。
Chironian

2018/09/21 05:39

> 多重継承ではなく、直列に継承した場合、virtual継承とするメリットって何かあるんですかね? 直列だけの継承ならメリットはないと思います。 > ・仮想基底関数のオーバーライドの際に、(後略) の件は、AをBとCが継承し、BとCをDが継承し、DがAの関数fooをオーバーライドし、更にDをEが継承するようなケースで意味があると思います。しかし、正直こんな複雑な継承関係を使いこなせる人は「神」と思います。私にはとても手が出ない領域です。
strike1217

2018/09/21 05:53

クラス階層や多重継承の設計の仕方は、オブジェクト指向の考え方が身についていないと結構難しいですよね。 クラスの継承の設計方法については、別途質問を立てた方が良さそうですね。
strike1217

2018/09/21 05:58

> privateなキャスト演算子が有るとき、暗黙の型変換されませんね。 すいません。ここがちょっと理解できないですね。 ここに出てくるキャスト演算子とは、型変換関数のことですよね? "operator 型名() { ... }" こんなヤツのことですよね。 > privateなキャスト演算子が有るとき、暗黙の型変換されませんね。 型変換関数が定義されていないクラスの場合、暗黙の型変換ができるということですか?
Chironian

2018/09/21 06:13

> ここに出てくるキャスト演算子とは、型変換関数のことですよね? その通りです。 ↓どちらかというとキャスト演算子と呼ぶ人の方が多そうです。 https://goo.gl/dzwyy4 https://goo.gl/MtFfn8 > 型変換関数が定義されていないクラスの場合、暗黙の型変換ができるということですか? ちょっと舌足らずでした。申し訳ない。 基底クラスへのキャスト演算子が暗黙的に定義され、非public継承だとそのキャスト演算子は非publicに定義されるので呼べない、public継承ならpublicに定義されるので呼べるという話をしたかったのです。
strike1217

2018/09/21 06:20

> キャスト演算子と呼ぶ人の方が多そうです。 そうなんですか! > 基底クラスへのキャスト演算子が暗黙的に定義され、非public継承だとそのキャスト演算子は非publicに定義されるので呼べない。 あ!もしかして、ユーザーがキャスト演算子を定義していない場合、コンパイラが暗黙の内にキャスト演算子を定義するんですか?
strike1217

2018/09/21 07:12

> 基底クラスへのキャスト演算子が暗黙的に定義 キャスト演算子(型変換関数)が暗黙の内に定義される場合なんてあるんですか!? それは初めて聞きました。
Chironian

2018/09/21 08:04

> 基底クラスへのキャスト演算子が暗黙的に定義 は、標準規格にて定義されると定められているかどうかは把握していないです。(publicな基底クラスへキャスト可能と定義されているのかも知れません。) 暗黙的に定義されると考えると振る舞いを理解しやすいという意味です。
strike1217

2018/09/21 08:20 編集

class Base {}; class Derived : private Base {}; Base *obj; Derived obj2; obj = (Base*)&obj2; 実験をしてみました。Cスタイルのキャストであれば、強制的に代入できるようです。 private継承、protected継承した場合、派生クラスのメンバ関数と基本クラスのメンバ関数のアクセス指定子が異なるから・・・だと考えていました。 Cスタイルキャストだと、「アクセス指定子などは考慮されない。 」そうです。 そう考えると、「アクセス指定子が異なるから」という理由で正しいような気がします。 https://cpplover.blogspot.com/2010/07/c.html
Chironian

2018/09/21 09:50

Cスタイル・キャストは問答無用です。そもそもキャスト不可能な場合でもキャストできますから、継承してなくても通りますよ。本当に邪悪です。 https://wandbox.org/permlink/jDaPEajxKG73hX6u
strike1217

2018/09/21 10:41

> 非public継承の場合、基底クラスは外部から見えない では、やはりこちらが正しいのですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問