46

派生クラスによってオーバーライドされる基本クラス メソッドを、派生クラス オブジェクトから呼び出すにはどうすればよいですか?

class Base{
  public:
    void foo(){cout<<"base";}
};

class Derived:public Base{
  public:
    void foo(){cout<<"derived";}
}

int main(){
  Derived bar;
  //call Base::foo() from bar here?
  return 0;
}
4

4 に答える 4

75

修飾された 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::fooDerived::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 です。)virtualBase::bar

修飾されたIDは、形式X :: Yまたは単にのいずれかです:: Y。の前の部分::は、識別子を検索する場所を指定しますY。最初の形式では、 を検索しX、次にのコンテキストY 内から検索します。XY2 番目の形式では、グローバル名前空間を検索します。

unqualified -idには が含まれていない::ため、(それ自体は) 名前を検索するコンテキストを指定しません。

式では、とb->fooの両方がunqualified-idsです。現在のコンテキストで検索されます (上記の例では関数です)。ローカル変数を見つけます。クラスメンバーアクセスの形をしているので、 (というか)の型の文脈から調べます。そこで、 の文脈から調べます。内部で宣言されているメンバー関数を見つけます。これを と呼びます。bfoobmainBase* bb->foofoob*bfooBasevoid foo()BaseBase::foo

についてfooは、これで完了です。 を呼び出しますBase::foo

についてb->barは、最初に を見つけますBase::barが、宣言されていvirtualます。ですので、仮想発送virtualを行っております。これにより、オブジェクトが指す型のクラス階層の最後の関数オーバーライドが呼び出されます。タイプ のオブジェクトを指しているため、最終的なオーバーライドはです。bbDerivedDerived::bar

のコンテキストfooから名前を調べると、 が見つかります。これが隠れると言われる所以です。のメンバ関数内で orなどの式を単純にorを使用すると、 のコンテキストから検索されます。DerivedDerived::fooDerived::foo Base::food.foo()Derivedfoo()this->foo()Derived

qualified-idを使用する場合、名前を検索する場所のコンテキストを明示的に示します。式は、コンテキストからBase::foo名前を検索することを示しています (たとえば、継承された関数を見つけることができます)。さらに、仮想ディスパッチを無効にします。fooBaseBase

したがって、それ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_funprivate_fun

ちなみに、 のエラーはのエラーとp.Base::private_fun()は異なりますr.Base::public_fun()。最初のエラーは、すでに名前の参照に失敗していますBase::private_fun(個人名であるため)。2 番目のものは、(本質的に) -pointerのrからPrivate_derived&への変換に失敗します。これが、2 番目が 内またはの友人から機能する理由です。Base&thisPrivate_derivedPrivate_derived

于 2013-04-06T16:13:55.653 に答える
18

まず、Derived は Base から継承する必要があります。

 class Derived : public Base{

そうは言っても

まず、Derived に foo を含めることはできません

class Base{
  public:
    void foo(){cout<<"base";}
};

class Derived : public Base{

}

int main(){
  Derived bar;
  bar.foo() // calls Base::foo()
  return 0;
}

次に、Derived::foo に Base::foo を呼び出させることができます。

class Base{
  public:
    void foo(){cout<<"base";}
};

class Derived : public Base{
  public:
    void foo(){ Base::foo(); }
                ^^^^^^^^^^
}

int main(){
  Derived bar;
  bar.foo() // calls Base::foo()
  return 0;
}

3 番目に、Base::foo の修飾 ID を使用できます

 int main(){
    Derived bar;
    bar.Base::foo(); // calls Base::foo()
    return 0;
 }
于 2013-04-06T16:11:13.437 に答える
3

foo()最初に仮想化を検討してください。

class Base {
public:
    virtual ~Base() = default;

    virtual void foo() { … }
};

class Derived : public Base {
public:
    virtual void foo() override { … }
};

ただし、これは仕事をします:

int main() {
    Derived bar;
    bar.Base::foo();
    return 0;
}
于 2013-04-06T16:12:08.107 に答える