[解決済み】派生クラスでオーバーライドされた関数が、ベースクラスの他のオーバーロードを隠してしまうのはなぜですか?
質問
コードを考えてみましょう。
#include <stdio.h>
class Base {
public:
virtual void gogo(int a){
printf(" Base :: gogo (int) \n");
};
virtual void gogo(int* a){
printf(" Base :: gogo (int*) \n");
};
};
class Derived : public Base{
public:
virtual void gogo(int* a){
printf(" Derived :: gogo (int*) \n");
};
};
int main(){
Derived obj;
obj.gogo(7);
}
このエラーが発生しました。
>g++ -pedantic -Os test.cpp -o test test.cpp: 関数 `int main()' 内にあります。 test.cpp:31: error: no matching function for call to `Derived::gogo(int)'. test.cpp:21: 注意: 候補は以下の通り: virtual void Derived::gogo(int*) test.cpp:33:2: 警告: ファイル末尾に改行がない >終了コードです。1
ここでは、Derivedクラスの関数が、ベースクラス内の同名(シグネチャではない)関数をすべて食ってしまっています。C++のこの挙動は、なぜか問題ないように見える。ポリモーフィックではない。
どうすればいい?
ご質問の文言から判断すると("hide"という単語を使っています)、ここで何が起こっているかはすでにお分かりでしょう。この現象は、「名前隠し」と呼ばれています。なぜか、誰かが質問するたびに なぜ と言って、その仕組みを説明するか(これはもう知っていることでしょう)、それを上書きする方法を説明するか(これは聞かれなかったことでしょう)ですが、誰も実際の "why" の質問を扱うことに関心がないようです。
名前隠しの決定、根拠、すなわち なぜ これは、継承されたオーバーロード関数が、与えられたクラスの現在のオーバーロード関数と混在することを許した場合、ある種の直感に反する、予期しない、潜在的に危険な動作を回避するために、C++に実際に設計されました。C++のオーバーロード解決は、候補のセットから最適な関数を選択することで行われることはご存知でしょう。これは、引数の型とパラメーターの型を照合することで行われる。このマッチングルールは時に複雑で、準備のないユーザーには非論理的と思われる結果を導くこともしばしばです。また、既存の関数群に新しい関数を追加すると、過負荷解消の結果が大幅に変化することがあります。
例えば、ベースクラスである
B
は、メンバ関数
foo
という型のパラメータを受け取り、そのパラメータが
void *
へのすべての呼び出しは
foo(NULL)
に解決されます。
B::foo(void *)
. 名前を隠していない状態で、この
B::foo(void *)
から下降する多くの異なるクラスで表示されます。
B
. しかし、ある[間接的、遠隔的]な子孫において、例えば
D
のクラス
B
関数
foo(int)
が定義されています。さて、名前を隠さないまま
D
は両方の
foo(void *)
と
foo(int)
可視化され、オーバーロードの解決に参加します。への呼び出しはどの関数になるのでしょうか?
foo(NULL)
型のオブジェクトを通して行われた場合、その解決は
D
? に解決されます。
D::foo(int)
というのは
int
の方が、積分ゼロにマッチしている(つまり
NULL
)は、どのポインタ型よりも優れています。そのため、階層全体を通して
foo(NULL)
は1つの関数に解決するのに対し
D
(以下)突然別のものに解決してしまうのです。
また、別の例として C++の設計と進化 77ページ
class Base {
int x;
public:
virtual void copy(Base* p) { x = p-> x; }
};
class Derived : public Base{
int xx;
public:
virtual void copy(Derived* p) { xx = p->xx; Base::copy(p); }
};
void f(Base a, Derived b)
{
a.copy(&b); // ok: copy Base part of b
b.copy(&a); // error: copy(Base*) is hidden by copy(Derived*)
}
このルールがないと、bの状態が部分的に更新され、スライスにつながる。
この挙動は、言語設計時に好ましくないと判断された。つまり、各クラスは宣言するメソッド名について「クリーンシート」から始めるという仕様です。この動作をオーバーライドするには、ユーザーによる明示的な操作が必要です。元々は継承されたメソッドの再宣言(現在は非推奨)でしたが、現在は明示的に using-declaration を使用するようになっています。
あなたが最初の投稿で正しく指摘したように(私は"Not polymorphic"の発言を参照しています)、この動作はクラス間のIS-A関係の違反とみなされるかもしれません。しかし、どうやら当時は、最終的に名前の隠蔽はより小さな悪であることが証明されると判断されたようです。
関連
-
[解決済み】C++のGetlineの問題(オーバーロードされた関数 "getline "のインスタンスがない
-
[解決済み】'cout'は型名ではない
-
[解決済み】エラー:strcpyがこのスコープで宣言されていない
-
[解決済み】c++でstd::vectorを返すための効率的な方法
-
[解決済み】Visual C++で "Debug Assertion failed "の原因となる行を見つける。
-
[解決済み】オブジェクト引数のない非静的メンバ関数の呼び出し コンパイラーエラー
-
[解決済み】クラステンプレートの使用にはテンプレート引数リストが必要です
-
[解決済み】浮動小数点数の乱数生成
-
[解決済み] ベースクラスのコンストラクタを呼び出す際のルールは?
-
[解決済み] 派生クラス関数から親クラス関数を呼び出すには?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] error: 'if' の前に unqualified-id を期待した。
-
[解決済み] 既に.objで定義されている-二重包含はない
-
[解決済み】cc1plus:エラー:g++で認識されないコマンドラインオプション"-std=c++11"
-
[解決済み】エラー。switchステートメントでcaseラベルにジャンプする
-
[解決済み】Visual C++で "Debug Assertion failed "の原因となる行を見つける。
-
[解決済み】Visual Studio 2013および2015でC++コンパイラーエラーC2280「削除された関数を参照しようとした」が発生する
-
[解決済み】#include<iostream>は存在するのですが、「識別子 "cout "は未定義です」というエラーが出ます。なぜですか?
-
[解決済み】エラー:free(): 次のサイズが無効です(fast)。
-
[解決済み] [Solved] インクルードファイルが開けません。'stdio.h' - Visual Studio Community 2017 - C++ Error
-
[解決済み】なぜ、サイズ8の初期化されていない値を使用するのでしょうか?