Чи можна викликати віртуальну функцію базового класу, якщо я її переосмислюю?


340

Скажіть, у мене є заняття Fooта Barналаштовано так:

class Foo
{
public:
    int x;

    virtual void printStuff()
    {
        std::cout << x << std::endl;
    }
};

class Bar : public Foo
{
public:
    int y;

    void printStuff()
    {
        // I would like to call Foo.printStuff() here...
        std::cout << y << std::endl;
    }
};

Як зазначено в коді, я хотів би мати можливість викликати функцію базового класу, яку я переосмислюю. У Java є super.funcname()синтаксис. Чи можливо це на C ++?



1
Для Googlers: зауважте, що у вас можуть виникнути такі проблеми, як у мене зі збереженням його як змінної члена класу, яка не є вказівником. Дивіться мою відповідь тут: stackoverflow.com/questions/4798966/… Я долучив новий / видалити, щоб виправити.
Андрій

Відповіді:


449

Синтаксис C ++ такий:

class Bar : public Foo {
  // ...

  void printStuff() {
    Foo::printStuff(); // calls base class' function
  }
};

11
Чи можливі проблеми з цим? Це погана практика?
Robben_Ford_Fan_boy

36
@David: Ні, це абсолютно нормально робити, хоча це може залежати від вашого фактичного класу, якщо воно дійсно корисне. Викликайте метод базового класу лише у випадку, якщо ви хочете щось статися;).
sth

20
обережно, це кодовий запах, коли ваші клієнти ПОТРІБНО роблять це! (зателефонував call super requirement, перевірте це тут: en.wikipedia.org/wiki/Call_super )
v.oddou

8
@ v.oddou: Немає нічого поганого в тому, щоб викликати суперклас, коли це корисно. Ця сторінка, на яку ви пов’язали, стосується певної конкретної потенційної проблеми, пов’язаної зі спадщиною. Це навіть само по собі говорить про те, що немає нічого поганого у виклику суперкласу взагалі: "Зауважте, що саме вимога виклику батьків є анти-шаблоном. У реальному коді є багато прикладів, де метод у підкласі все ще може бути хочуть функціональність суперкласу, зазвичай там, де він лише збільшує функціональність батьків. "
sth

26
Для більшості це може бути очевидним, але для повноти пам’ятайте, що ніколи цього не роблять у конструкторах та деструкторах.
TigerCoding

123

так,

class Bar : public Foo
{
    ...

    void printStuff()
    {
        Foo::printStuff();
    }
};

Це те саме, що і superв Java, за винятком того, що дозволяє викликати реалізації з різних баз, коли у вас є багатократне успадкування.

class Foo {
public:
    virtual void foo() {
        ...
    }
};

class Baz {
public:
    virtual void foo() {
        ...
    }
};

class Bar : public Foo, public Baz {
public:
    virtual void foo() {
        // Choose one, or even call both if you need to.
        Foo::foo();
        Baz::foo();
    }
};

7
Це краща відповідь, ніж обрана. Дякую.
Божевільний фізик

Багаторазове успадкування? Ой поступається!
ламіно

69

Іноді вам потрібно викликати реалізацію базового класу, коли ви не перебуваєте у похідній функції ... Це все ще працює:

struct Base
{
    virtual int Foo()
    {
        return -1;
    }
};

struct Derived : public Base
{
    virtual int Foo()
    {
        return -2;
    }
};

int main(int argc, char* argv[])
{
    Base *x = new Derived;

    ASSERT(-2 == x->Foo());

    //syntax is trippy but it works
    ASSERT(-1 == x->Base::Foo());

    return 0;
}

2
Зауважте, що вам потрібно xмати тип Base*і використовувати Base::Fooдля цього синтаксис
TTimo

27

Про всяк випадок, якщо ви робите це для багатьох функцій у своєму класі:

class Foo {
public:
  virtual void f1() {
    // ...
  }
  virtual void f2() {
    // ...
  }
  //...
};

class Bar : public Foo {
private:
  typedef Foo super;
public:
  void f1() {
    super::f1();
  }
};

Це може заощадити трохи написання, якщо ви хочете перейменувати Foo.


1
Веселий спосіб це зробити, але не вийде з багаторазовим успадкуванням.
Божевільний фізик

5
А що робити, якщо у вас більше 1 базового класу? У будь-якому разі, дурно і часто марно намагатися зігнути C ++, щоб виглядати якоюсь іншою мовою. Просто запам’ятайте ім’я бази та називайте це.
підкреслюй_d

6

Якщо ви хочете викликати функцію базового класу з його похідного класу, ви можете просто зателефонувати всередину перекритої функції із згадуванням імені базового класу (наприклад Foo :: printStuff () ).

код іде тут

#include <iostream>
using namespace std;

class Foo
{
public:
    int x;

    virtual void printStuff()
    {
         cout<<"Base Foo printStuff called"<<endl;
    }
};

class Bar : public Foo
{
public:
    int y;

    void printStuff()
    {
        cout<<"derived Bar printStuff called"<<endl;
        Foo::printStuff();/////also called the base class method
    }
};

int main()
{
    Bar *b=new Bar;
    b->printStuff();
}

Знову ви можете визначити під час виконання, яку функцію викликати, використовуючи об'єкт цього класу (похідний або базовий).

код нижче

#include <iostream>
using namespace std;

class Foo
{
public:
    int x;

    virtual void printStuff()
    {
         cout<<"Base Foo printStuff called"<<endl;
    }
};

class Bar : public Foo
{
public:
    int y;

    void printStuff()
    {
        cout<<"derived Bar printStuff called"<<endl;
    }
};

int main()
{

    Foo *foo=new Foo;
    foo->printStuff();/////this call the base function
    foo=new Bar;
    foo->printStuff();
}

0

Перевір це...

#include <stdio.h>

class Base {
public:
   virtual void gogo(int a) { printf(" Base :: gogo (int) \n"); };    
   virtual void gogo1(int a) { printf(" Base :: gogo1 (int) \n"); };
   void gogo2(int a) { printf(" Base :: gogo2 (int) \n"); };    
   void gogo3(int a) { printf(" Base :: gogo3 (int) \n"); };
};

class Derived : protected Base {
public:
   virtual void gogo(int a) { printf(" Derived :: gogo (int) \n"); };
   void gogo1(int a) { printf(" Derived :: gogo1 (int) \n"); };
   virtual void gogo2(int a) { printf(" Derived :: gogo2 (int) \n"); };
   void gogo3(int a) { printf(" Derived :: gogo3 (int) \n"); };       
};

int main() {
   std::cout << "Derived" << std::endl;
   auto obj = new Derived ;
   obj->gogo(7);
   obj->gogo1(7);
   obj->gogo2(7);
   obj->gogo3(7);
   std::cout << "Base" << std::endl;
   auto base = (Base*)obj;
   base->gogo(7);
   base->gogo1(7);
   base->gogo2(7);
   base->gogo3(7);

   std::string s;
   std::cout << "press any key to exit" << std::endl;
   std::cin >> s;
   return 0;
}

вихід

Derived
 Derived :: gogo (int)
 Derived :: gogo1 (int)
 Derived :: gogo2 (int)
 Derived :: gogo3 (int)
Base
 Derived :: gogo (int)
 Derived :: gogo1 (int)
 Base :: gogo2 (int)
 Base :: gogo3 (int)
press any key to exit

найкращий спосіб - використовувати функцію base :: як скажіть @sth


Як пояснено в цьому запитанні , це не повинно працювати через protectedспадщину. Для передачі покажчика базового класу ви повинні використовувати публічне успадкування.
Darkproduct

Цікаво. Прочитавши цю відповідь, я подумав із захищеним успадкуванням той факт, що похідне похідне від Base було б видно лише для самого класу, а також не видно і зовні.
Darkproduct

0

Так, ви можете це назвати. Синтаксис C ++ для виклику функції батьківського класу в дочірньому класі є

class child: public parent {
  // ...

  void methodName() {
    parent::methodName(); // calls Parent class' function
  }
};

Детальніше про перевизначення функції .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.