Подтвердить что ты не робот

Метод базового класса вызова из объекта производного класса

Как я могу вызвать метод базового класса, который переопределяется производным классом из объекта производного класса?

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;
}
4b9b3361

Ответ 1

Вы всегда можете (*) ссылаться на функцию базового класса с помощью идентификатора qualit:

#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, если оно подпишится на Base::bar.)

Квалифицированный идентификатор является либо формой X :: Y, либо просто :: Y. Часть перед :: указывает, где мы хотим найти идентификатор Y. В первом виде мы смотрим вверх X, затем смотрим вверх Y из контекста X. Во второй форме мы просматриваем Y в глобальном пространстве имен.

Неквалифицированный-id не содержит :: и поэтому не определяет (сам) контекст, где искать имя.

В выражении b->foo оба 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, мы выполняем виртуальную отправку. Это вызовет конечную функцию overrider в иерархии классов типа объекта b. Поскольку b указывает на объект типа Derived, конечный переадресатор Derived::bar.

При поиске имени foo из Derived контекста мы найдем Derived::foo. Вот почему Derived::foo, как говорят, скрывает Base::foo. Выражения, такие как d.foo() или внутри функции-члена Derived, используя просто foo() или this->foo(), будут искать из контекста Derived.

При использовании квалифицированного идентификатора мы явно указываем, где искать имя. Выражение Base::foo указывает, что мы хотим найти имя foo из контекста Base (он может найти функции, которые Base унаследовал, например). Кроме того, он отключает виртуальную отправку.

Следовательно, d.Base::foo() найдет Base::foo и назовет его; d.Base::bar() найдет Base::bar и назовет его.


Интересный факт: чистые виртуальные функции могут иметь реализацию. Они не могут быть вызваны через виртуальную отправку, потому что их нужно переопределить. Тем не менее, вы все равно можете вызвать их реализацию (если они есть), используя идентификатор с квалификацией.

#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
}

Обратите внимание, что спецификаторы доступа как для членов класса, так и для базовых классов влияют на то, можно ли использовать функцию для определения типа базового класса для объекта производного типа.

Например:

#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
}

Доступность ортогональна поиску имен. Таким образом, скрытие имени не влияет на него (вы можете оставить public_fun и private_fun в производных классах и получить одинаковое поведение и ошибки для вызовов с квалифицированным идентификатором).

Ошибка в p.Base::private_fun() отличается от ошибки в r.Base::public_fun() тем самым: первый уже не ссылается на имя Base::private_fun (потому что это личное имя). Второй не может преобразовать r из Private_derived& в Base& для this -потока (по существу). Вот почему вторая работает из Private_derived или друга Private_derived.

Ответ 2

Прежде всего, Derived должен наследовать от Base.

 class Derived : public Base{

Тем не менее

Сначала вы можете просто не иметь foo в Derived

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

class Derived : public Base{

}

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

Во-вторых, вы можете сделать Derived:: foo call 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;
}

В-третьих, вы можете использовать квалифицированный идентификатор Base:: foo

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

Ответ 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;
}

Ответ 4

Важная записка [дополнительная]: у вас все еще будут ошибки компиляции, если произойдет Сокрытие имени.

В этом случае используйте либо ключевое слово using, либо используйте qualifer. Кроме того, см. этот ответ.

#include <iostream>

class Base{
  public:
    void foo(bool bOne, bool bTwo){std::cout<<"base"<<bOne<<bTwo;}
};

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

int main()
{
  Derived bar;
  //bar.foo(true,true);      // error:    derived func attempted
  bar.foo(true);             // no error: derived func
  bar.Base::foo(true,true);  // no error: base func, qualified
  return 0;
}