2016-04-17 4 views
0

現在、C++の継承で名前隠蔽を理解しようとしています。同じ名前の派生クラス関数によって基底クラス関数が隠されていなかった理由

私は継承されたメソッドを再定義すると、オーバーロードの変動はありませんが、C++プライマープラス、ページ要するに744

でこれを読みました。派生クラスで関数を再定義すると、同じ関数シグネチャで基本クラス宣言をオーバーライドするだけではありません。代わりに、引数シグネチャに関係なく、同じ名前のすべての基本クラスメソッドを非表示にします。

だから私は試してみました、ここに私の二つのクラス、ベースがあり、そして本によると

class Base{ 
public: 
    Base(){ 
     cout << "Base::Base() called\n"; 
    } 

    virtual ~Base(){} 

    virtual void output(int x){ 
     cout << "Base::output(int x) called\n"; 
    } 

    virtual void output(double x){ 
     cout << "Base::output(double x) called\n"; 
    } 

    virtual void output(char x){ 
     cout << "Base::output(char x) called\n"; 
    } 

    virtual void output(string x){ 
     cout << "Base::ouput(string x) called\n"; 
    } 
}; 


class Derived : public Base{ 
public: 
    Derived():Base(){ 
     cout << "Derived::Derived() called\n"; 
    } 

    virtual ~Derived(){} 

    virtual void output(string x){ 
     cout << "Derived::ouput(string x) called\n"; 
    } 

}; 

を派生、void Base::output(string x)が再定義され、同じ名前を持つ他のすべてのメンバ関数が非表示になります。

しかし、次の2つのコードは、異なるストーリーを示しています。

Derived* x = new Derived(); 
x->output(7); 

予想通り、このコードはコンパイルできない、エラーメッセージが

ある

'文字列' から 'INT' からノー実行可能な変換しかし、私はこのように行う場合、

Base* x = new Derived(); 
x->output(7); 

魔法のように働いて、次の出力を与えました:

ベース::ベース()派生
と呼ばれる::派生()
ベースと呼ばれる::出力(int型x)は、これが起こる可能性がなぜ、ポインタ型が実際に影響を与えない

と呼ばれます見上げる? Btw、これはXcodeにありました。

+0

'output(string)'関数はどちらも "Derived :: output(string)"を出力します。 – kfsone

+0

@kfsone申し訳ありません、それはタイプミスでした – Curtis2

答えて

0

あなたは

Base* x = new Derived(); 
x->output(7); 

を使用する場合Derivedの関数はコンパイル時に見上げていません。 Baseの関数だけがコンパイル時に参照されます。したがって、それは動作します。

x->output(7); 

が処理されると

xの種類について私たちが知っている唯一のものは、それがBase*であるということです。実際にDerivedオブジェクトを指しているという事実は、コンパイル時には考慮されません。コンパイル時に、その呼び出しはBase::outout(int)に解決されます。 Base::output(int)Derivedでオーバーライドされないため、実行時にBase::output(int)も実行されます。 Derived::output(int)があった場合は、実行時に実行されていました。

+0

私はそれが正しいのDerivedのvtableを参照すべきだと思った? – Curtis2

+0

vtableは実行時にのみ検索され、 'virtual'メンバ関数に対しても検索されます。 'Base :: output(int)'の呼び出しはコンパイル時に解決されます。 'Base :: output(int)'は 'Derived'でオーバーライドされないので、' Base :: output(int) 'は実行時に実行されます。 –

+0

Base :: output(int)がコンパイル時に解決された場合でも、基本クラスポインタが指すすべての派生オブジェクトは、派生オブジェクトの代わりに基本クラス関数を呼び出すことになります。実際のオブジェクトタイプ考慮されていませんでした。 – Curtis2

0

はい、ポインタタイプはルックアップに影響します。つまり、タイプを持つ全体のポイントです。Base*を定義すると、すべてのコンパイラが、タイプBaseのオブジェクトを指していることを知っています。Derivedの可能性は考慮されません。このコードの場合

+0

このように、この基底クラス関数隠蔽物は、基本クラスポインタを使用することによってバイパスできますか? – Curtis2

+0

@ Curtis2絶対に。それは「ベース」ではなく、「派生」の観点からのみ隠されています。 –

+0

ああ、私は、このオブジェクトの実際のタイプが派生であっても、ベースタイプのポインタは、ベースのパースペクティブから見えるようにします。 – Curtis2

0

Derived* x = new Derived(); 
x->output(7); 

コンパイラは最初の関数を解決しようとするとそれが派生出力と呼ばれる機能を持っていることを見ています。これが見つかると、コンパイラはそれ以上見ていないでしょう。次に、引数を解決し、引数が正しくないことを確認します。

Derivedクラスポインタを使用してベースクラスの関数にアクセスしているため、どちらの方法でもレイトバインディングは適用されません。あなたはアップキャストする必要があります。このコード

Base* x = new Derived(); 
x->output(7); 

コンパイラは、xが整数の引数を持つ関数を有する基本型であることを見ます。コンパイラがこの関数を解決し、設定されています。多態的な振る舞いの関数をオーバーライドした場合は、これが適切な使い方になります。

x-> output( "yes")を呼び出すことができます。正しい関数が呼び出されます。これはDerivedバージョンになります。ここでvtableが使用されます。

コンパイラが「脆弱な基底クラスの問題」を調べる理由のために。

関連する問題