修飾された ID を使用して、いつでも (*) 基本クラスの関数を参照できます。
#include <iostream>
class Base{
public:
void foo(){std::cout<<"base";}
};
class Derived : public Base
{
public:
void foo(){std::cout<<"derived";}
};
int main()
{
Derived bar;
//call Base::foo() from bar here?
bar.Base::foo(); // using a qualified-id
return 0;
}
【OPの誤字脱字も修正しました。】
(*) アクセス制限は引き続き適用され、基本クラスがあいまいになる可能性があります。
そうBase::foo
でない場合virtual
、オーバーライドDerived::foo
しません。むしろ、隠します。違いは、次の例で確認できます。 Base::foo
Derived::foo
Base::foo
struct Base {
void foo() { std::cout << "Base::foo\n"; }
virtual void bar() { std::cout << "Base::bar\n"; }
};
struct Derived : Base {
void foo() { std::cout << "Derived::foo\n"; }
virtual void bar() { std::cout << "Derived::bar\n"; }
};
int main() {
Derived d;
Base* b = &d;
b->foo(); // calls Base::foo
b->bar(); // calls Derived::bar
}
(は、その署名が と互換性がある限りDerived::bar
、キーワードを使用しなくても暗黙的に virtual です。)virtual
Base::bar
修飾されたIDは、形式X :: Y
または単にのいずれかです:: Y
。の前の部分::
は、識別子を検索する場所を指定しますY
。最初の形式では、 を検索しX
、次にのコンテキストY
内から検索します。X
Y
2 番目の形式では、グローバル名前空間を検索します。
unqualified -idには が含まれていない::
ため、(それ自体は) 名前を検索するコンテキストを指定しません。
式では、とb->foo
の両方がunqualified-idsです。現在のコンテキストで検索されます (上記の例では関数です)。ローカル変数を見つけます。クラスメンバーアクセスの形をしているので、 (というか)の型の文脈から調べます。そこで、 の文脈から調べます。内部で宣言されているメンバー関数を見つけます。これを と呼びます。b
foo
b
main
Base* b
b->foo
foo
b
*b
foo
Base
void foo()
Base
Base::foo
についてfoo
は、これで完了です。 を呼び出しますBase::foo
。
についてb->bar
は、最初に を見つけますBase::bar
が、宣言されていvirtual
ます。ですので、仮想発送virtual
を行っております。これにより、オブジェクトが指す型のクラス階層の最後の関数オーバーライドが呼び出されます。タイプ のオブジェクトを指しているため、最終的なオーバーライドはです。b
b
Derived
Derived::bar
のコンテキストfoo
から名前を調べると、 が見つかります。これが隠れると言われる所以です。のメンバ関数内で orなどの式を単純にorを使用すると、 のコンテキストから検索されます。Derived
Derived::foo
Derived::foo
Base::foo
d.foo()
Derived
foo()
this->foo()
Derived
qualified-idを使用する場合、名前を検索する場所のコンテキストを明示的に示します。式は、コンテキストからBase::foo
名前を検索することを示しています (たとえば、継承された関数を見つけることができます)。さらに、仮想ディスパッチを無効にします。foo
Base
Base
したがって、それd.Base::foo()
を見つけBase::foo
て呼び出します。d.Base::bar()
それを見つけBase::bar
て呼び出します。
豆知識: 純粋仮想関数は実装を持つことができます。オーバーライドする必要があるため、仮想ディスパッチ経由で呼び出すことはできません。ただし、qualified-idを使用して実装を呼び出すことはできます (実装がある場合) 。
#include <iostream>
struct Base {
virtual void foo() = 0;
};
void Base::foo() { std::cout << "look ma, I'm pure virtual!\n"; }
struct Derived : Base {
virtual void foo() { std::cout << "Derived::foo\n"; }
};
int main() {
Derived d;
d.foo(); // calls Derived::foo
d.Base::foo(); // calls Base::foo
}
クラス メンバーと基本クラスの両方のアクセス指定子は、派生型のオブジェクトで基本クラスの関数を呼び出すために修飾 ID を使用できるかどうかに影響を与えることに注意してください。
例えば:
#include <iostream>
struct Base {
public:
void public_fun() { std::cout << "Base::public_fun\n"; }
private:
void private_fun() { std::cout << "Base::private_fun\n"; }
};
struct Public_derived : public Base {
public:
void public_fun() { std::cout << "Public_derived::public_fun\n"; }
void private_fun() { std::cout << "Public_derived::private_fun\n"; }
};
struct Private_derived : private Base {
public:
void public_fun() { std::cout << "Private_derived::public_fun\n"; }
void private_fun() { std::cout << "Private_derived::private_fun\n"; }
};
int main() {
Public_derived p;
p.public_fun(); // allowed, calls Public_derived::public_fun
p.private_fun(); // allowed, calls Public_derived::public_fun
p.Base::public_fun(); // allowed, calls Base::public_fun
p.Base::private_fun(); // NOT allowed, tries to name Base::public_fun
Private_derived r;
r.Base::public_fun(); // NOT allowed, tries to call Base::public_fun
r.Base::private_fun(); // NOT allowed, tries to name Base::private_fun
}
アクセシビリティは、名前の検索と直交しています。したがって、名前の隠蔽はそれに影響を与えません (派生クラスを省略して、qualified-id 呼び出しで同じ動作とエラーを得ることができます) public_fun
。private_fun
ちなみに、 のエラーはのエラーとp.Base::private_fun()
は異なりますr.Base::public_fun()
。最初のエラーは、すでに名前の参照に失敗していますBase::private_fun
(個人名であるため)。2 番目のものは、(本質的に) -pointerのr
からPrivate_derived&
への変換に失敗します。これが、2 番目が 内またはの友人から機能する理由です。Base&
this
Private_derived
Private_derived