Как вызвать функцию родительского класса из производной функции класса?

604

Как мне вызвать родительскую функцию из производного класса, используя C ++? Например, у меня есть класс с именем parentи класс, childкоторый является производным от parent. Внутри каждого класса есть printфункция. В определении функции печати ребенка я хотел бы вызвать функцию печати родителей. Как бы я поступил так?

IaCoder
источник
Все вышеприведенные решения предполагают, что ваша функция печати является статическим методом. Это тот случай? Если метод не является статичным, то приведенные выше решения не актуальны.
hhafez
14
hhafez, вы ошибаетесь, синтаксис base :: function () выглядит как статический синтаксис вызова метода, но он работает для методов экземпляра в этом контексте.
Мотти
2
Я бы не стал использовать MSVC __super, поскольку он зависит от платформы. Хотя ваш код может не работать на любой другой платформе, я бы воспользовался другими советами, поскольку они делают это так, как предполагалось.
тизер
Антипаттерном, где производные классы всегда требуются для вызова функций родительского класса, является Вызов super
Rufus

Ответы:

775

Я рискну заявить об очевидном: вы вызываете функцию, если она определена в базовом классе, она автоматически доступна в производном классе (если только private ).

Если в производном классе есть функция с такой же сигнатурой, вы можете устранить ее неоднозначность, добавив имя базового класса, за которым следуют два двоеточия base_class::foo(...). Вы должны заметить, что в отличие от Java и C #, C ++ не имеет ключевого слова для «базового класса» ( superили base), поскольку C ++ поддерживает множественное наследование, что может привести к неоднозначности.

class left {
public:
    void foo();
};

class right {
public:
    void foo();
};

class bottom : public left, public right {
public:
    void foo()
    {
        //base::foo();// ambiguous
        left::foo();
        right::foo();

        // and when foo() is not called for 'this':
        bottom b;
        b.left::foo();  // calls b.foo() from 'left'
        b.right::foo();  // call b.foo() from 'right'
    }
};

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

class bottom : public left, public left { // Illegal
};
Моти
источник
31
Почему вы хотели бы наследовать от одного и того же класса дважды?
Павел Бревчински
65
@bluesm: в классическом ООП это не имеет особого смысла, но в универсальном программировании template<class A, class B> class C: public A, public B {};два типа могут быть одинаковыми по причинам, в зависимости от того, как используется ваш код (что делает А и В одинаковыми), может быть два или три уровень абстракции от кого-то, кто не знает, что вы сделали.
Эмилио Гаравалья
8
Я думаю , что это полезно добавить, что это будет вызывать родительский метод класса , даже если она не реализуется непосредственно в родительском классе, но будет реализован в одном из родительских классов в цепочке наследования.
Максим Лавров
4
На sidenote, я разозлился, когда я попытался поместить это в файл cpp. Я использовал 'namespace std'. 'left' определяется где-то в этом пространстве имен. Пример не скомпилируется - сводил меня с ума :). Затем я изменил «левый» на «левый». Отличный пример, кстати.
Матхай
72
@ Mathai И вот почему вы не должны использовать using namespace std.
JAB
193

Учитывая родительский класс named Parentи дочерний класс named Child, вы можете сделать что-то вроде этого:

class Parent {
public:
    virtual void print(int x);
}

class Child : public Parent {
    void print(int x) override;
}

void Parent::print(int x) {
    // some default behavior
}

void Child::print(int x) {
    // use Parent's print method; implicitly passes 'this' to Parent::print
    Parent::print(x);
}

Обратите внимание, что Parentэто фактическое имя класса, а не ключевое слово.

Грег Хьюгилл
источник
Конечно, это было бы полезно только в том случае, если базовый вызов перемежался с другой логикой, иначе не было бы смысла переопределять функцию, так что, может быть, это немного слишком к месту;)
underscore_d
1
@underscore_d на самом деле, это полезно, даже если базовый вызов не перемежался с другой логикой. Допустим, родительский класс в значительной степени делает все, что вы хотите, но предоставляет метод foo (), который вы не хотите, чтобы пользователи child использовали, - либо потому, что foo () не имеет смысла для дочерних элементов, либо внешние вызывающие элементы для child будут испорчены, что такое child. делает. Поэтому child может использовать parent :: foo () в определенных ситуациях, но предоставить реализацию foo, чтобы они скрывали родительский foo () от вызова.
iheanyi
@iheanyi Звучит интересно, но извините, я еще не понимаю этого. Есть foo()здесь аналогична print()или отдельной функции? И вы имеете в виду, используя privateнаследование, чтобы скрыть детали, унаследованные от базы, и предоставляя publicфункции теневого копирования для вещей, которые вы хотите показать?
underscore_d
@underscore_d Да, foo()было аналогично print(). Позвольте мне вернуться к использованию, так print()как я думаю, что это будет иметь больше смысла в этом контексте. Допустим, кто-то создал класс, который выполнил некоторый набор операций с определенным типом данных, открыл некоторые print(obj&)методы доступа и имел метод. Мне нужен новый класс, который работает, array-of-objно все остальное то же самое. Композиция приводит к большому количеству дублированного кода. Наследование сводит это к минимуму при print(array-of-obj&) вызове цикла print(obj&), но не хочет, чтобы клиенты звонили, print(obj&)потому что для них это не имеет смысла
iheanyi
@underscore_d Это основано на предположении, что я не могу реорганизовать общие части исходного родительского класса или что это невероятно дорого. Частное наследование может сработать, но тогда вы потеряете общедоступные средства доступа, на которые вы полагались, и, следовательно, должны будут дублировать код.
iheanyi
32

Если ваш базовый класс вызывается Base, а ваша функция вызывается, FooBar()вы можете вызвать его напрямую, используяBase::FooBar()

void Base::FooBar()
{
   printf("in Base\n");
}

void ChildOfBase::FooBar()
{
  Base::FooBar();
}
Эндрю Роллингс
источник
28

В MSVC для этого есть специальное ключевое слово Microsoft: __super


MSDN: позволяет явно указать, что вы вызываете реализацию базового класса для функции, которую вы переопределяете.

// deriv_super.cpp
// compile with: /c
struct B1 {
   void mf(int) {}
};

struct B2 {
   void mf(short) {}

   void mf(char) {}
};

struct D : B1, B2 {
   void mf(short) {
      __super::mf(1);   // Calls B1::mf(int)
      __super::mf('s');   // Calls B2::mf(char)
   }
};

Андрей
источник
5
Эх, я бы предпочел, typdefчтобы родитель был чем-то вроде super.
Томас Эдинг
26
Я не буду пытаться оправдать использование __super; Я упомянул это здесь как альтернативное предложение. Разработчики должны знать свой компилятор и понимать плюсы и минусы его возможностей.
Андрей
13
Я предпочел бы отговорить кого-либо от его использования, так как это сильно затрудняет переносимость кода.
Эрбурет говорит восстановить Монику
26
Я не согласен с Андреем: разработчики должны знать стандарт и не должны беспокоиться о функциях компилятора, если мы рассматриваем написание программного обеспечения, которое в первую очередь не зависит от компилятора, что я считаю хорошей идеей в любом случае, потому что рано или поздно в больших проектах многокомпиляторов в любом случае используются.
Габриэль
7
«Разработчики должны знать свой компилятор», именно из-за этого и включения нестандартных функций IE6 ...
e2-e4
5

Если модификатор доступа к функции-члену базового класса защищен ИЛИ общедоступен, вы можете вызвать функцию-член базового класса из производного класса. Можно вызвать не виртуальную и виртуальную функцию-член базового класса из производной функции-члена. Пожалуйста, обратитесь к программе.

#include<iostream>
using namespace std;

class Parent
{
  protected:
    virtual void fun(int i)
    {
      cout<<"Parent::fun functionality write here"<<endl;
    }
    void fun1(int i)
    {
      cout<<"Parent::fun1 functionality write here"<<endl;
    }
    void fun2()
    {

      cout<<"Parent::fun3 functionality write here"<<endl;
    }

};

class Child:public Parent
{
  public:
    virtual void fun(int i)
    {
      cout<<"Child::fun partial functionality write here"<<endl;
      Parent::fun(++i);
      Parent::fun2();
    }
    void fun1(int i)
    {
      cout<<"Child::fun1 partial functionality write here"<<endl;
      Parent::fun1(++i);
    }

};
int main()
{
   Child d1;
   d1.fun(1);
   d1.fun1(2);
   return 0;
}

Вывод:

$ g++ base_function_call_from_derived.cpp
$ ./a.out 
Child::fun partial functionality write here
Parent::fun functionality write here
Parent::fun3 functionality write here
Child::fun1 partial functionality write here
Parent::fun1 functionality write here
Аджай Ядав
источник
1
Спасибо, что привели несколько примеров с virtual!
М.Иоан
5

Вызовите родительский метод с оператором разрешения родительской области.

Родитель :: Способ ()

class Primate {
public:
    void whatAmI(){
        cout << "I am of Primate order";
    }
};

class Human : public Primate{
public:
    void whatAmI(){
        cout << "I am of Human species";
    }
    void whatIsMyOrder(){
        Primate::whatAmI(); // <-- SCOPE RESOLUTION OPERATOR
    }
};
Дин П
источник
-15
struct a{
 int x;

 struct son{
  a* _parent;
  void test(){
   _parent->x=1; //success
  }
 }_son;

 }_a;

int main(){
 _a._son._parent=&_a;
 _a._son.test();
}

Справочный пример.

superbem
источник
2
Не могли бы вы отредактировать объяснение, почему / как этот код отвечает на вопрос? Ответы только на код не приветствуются, потому что их не так легко выучить, как код с объяснением. Без объяснения требуется значительно больше времени и усилий, чтобы понять, что было сделано, изменения, внесенные в код, или, если код полезен. Это объяснение важно как для людей, пытающихся извлечь уроки из ответа, так и для тех, кто оценивает ответ, чтобы понять, является ли он действительным или стоит голосовать.
Макьен,
3
Этот ответ касается вложенных классов, тогда как вопрос был о производных классах (хотя слова 'parent' и 'child' немного вводят в заблуждение) и поэтому вообще не отвечает на вопрос.
Йоханнес Матокич