Как вызвать родительскую функцию из производного класса с помощью C++? Например, у меня есть класс parent и класс child, производный от parent. В
В каждом классе есть функция print. В определении дочерней функции печати я хотел бы вызвать родительскую функцию печати. Как бы я это сделал?
hhafez, вы ошибаетесь, синтаксис base :: function () выглядит как синтаксис вызова статического метода, но он работает для методов экземпляра в этом контексте.
Я бы не стал использовать MSVC __super, поскольку он зависит от платформы. Хотя ваш код может не работать на какой-либо другой платформе, я бы использовал другие предложения, поскольку они делают это в соответствии с предназначенным языком.
Возможный дубликат Могу ли я вызвать виртуальную функцию базового класса, если я ее переопределю?
Антипаттерн, в котором производные классы всегда требуется для вызова функций родительского класса, - Звоните супер





Учитывая родительский класс с именем Parent и дочерний класс с именем 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 на самом деле, это полезно, даже если базовый вызов не перемежается с другой логикой. Скажем, родительский класс в значительной степени делает все, что вы хотите, но предоставляет метод foo (), который вы не хотите, чтобы пользователи дочернего элемента использовали - либо потому, что foo () не имеет смысла для дочернего элемента, либо внешние вызывающие его дочерние объекты испортят то, что дочерний делает. Таким образом, дочерний элемент может использовать parent :: foo () в определенных ситуациях, но предоставить реализацию foo, чтобы скрыть родительский foo () от вызова.
@iheanyi Звучит интересно, но извините, я еще не понял. foo() здесь аналог print() или отдельная функция? И вы имеете в виду использование наследования private, чтобы скрыть детали, унаследованные от базы, и предоставление функций затенения public для вещей, которые вы делать хотите раскрыть?
@underscore_d Да, foo() был аналогом print(). Позвольте мне вернуться к использованию print(), поскольку я думаю, что это будет иметь больше смысла в этом контексте. Допустим, кто-то создал класс, который выполняет некоторый набор операций с определенным типом данных, предоставляет некоторые средства доступа и имеет метод print(obj&). Мне нужен новый класс, который работает на array-of-obj, но все остальное то же самое. Композиция приводит к дублированию кода. Наследование минимизирует это в цикле print(array-of-obj&), вызывающем print(obj&), но не хочет, чтобы клиенты вызывали print(obj&), потому что для них это не имеет смысла.
@underscore_d Это основано на предположении, что я не могу реорганизовать общие части исходного родительского класса или что это невероятно дорого. Частное наследование может работать, но тогда вы потеряете общедоступные методы доступа, на которые вы полагались, и, следовательно, вам потребуется дублировать код.
Если ваш базовый класс называется Base, а ваша функция называется FooBar(), вы можете вызвать ее напрямую, используя Base::FooBar().
void Base::FooBar()
{
printf("in Base\n");
}
void ChildOfBase::FooBar()
{
Base::FooBar();
}
Я рискну заявить очевидное: вы вызываете функцию, если она определена в базовом классе, она автоматически доступна в производном классе (если это не 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
};
Почему вы хотите наследовать один и тот же класс дважды?
@bluesm: в классическом ООП это не имеет особого смысла, но в универсальном программировании template<class A, class B> class C: public A, public B {}; может иметь два одинаковых типа по причинам, зависящим от того, как используется ваш код (что делает A и B одинаковыми), может быть два или три уровня абстракции от кого-то, кто не знает, что вы сделали.
Я думаю, полезно добавить, что это вызовет метод родительского класса, даже если он не реализован непосредственно в родительском классе, а является реализован в одном из родительских классов в цепочке наследования.
что, если родительский метод использует это?
@martinkunev, если базовый класс использует это, то он работает нормально для обычных методов и подавляет динамический вызов методов virtual.
Кстати, я разозлился, когда попытался поместить это в файл cpp. У меня было использование пространства имен std. "left" определено где-то в этом пространстве имен. Пример не компилировался - сводил меня с ума :). Затем я изменил «левый» на «левый». Кстати, отличный пример.
@Mathai Вот почему вы не должны использовать using namespace std.
+1 за указание You should note that unlike Java and C#, C++ does not have a keyword for "the base class".
Вы можете добавить typedef left super; в свой класс, если хотите, тогда вы можете обратиться к super::foo();. И поскольку вы не можете дважды наследовать один и тот же класс напрямую, вам придется делать это косвенно struct left1 : left{}; struct left2 : left{}; class bottom : public left1, public left2 { ... };, но вам, вероятно, лучше использовать композицию, а не наследование.
что произойдет, если foo будет виртуальной функцией в обоих базовых классах?
В MSVC для этого есть специальное ключевое слово Microsoft: __супер
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)
}
};
Эх, я бы предпочел, чтобы typdef использовал родительский элемент как что-то вроде super.
Я не буду оправдывать использование __super; Я упомянул это здесь как альтернативное предложение. Разработчики должны знать свой компилятор и понимать плюсы и минусы его возможностей.
Я бы предпочел отговорить кого-либо от его использования, поскольку это серьезно затрудняет переносимость кода.
Я не согласен с Андреем: разработчики должны знать стандарт и не должны беспокоиться о функциях компилятора, если мы рассматриваем возможность написания программного обеспечения, которое в основном не зависит от компилятора, что, на мой взгляд, в любом случае является хорошей идеей, потому что рано или поздно в больших проектах несколько компиляторов в любом случае используются.
@ThomasEding Я бы не предпочел ни того, ни другого, потому что C++ - это не Java, и на самом деле нетрудно запомнить имя родителя, плюс это требуется при использовании MI.
Это действительно плохая идея, всегда старайтесь избегать специфических функций компилятора, или они действительно необходимы, используйте их внутри #defines, где вы можете справиться стандартным и нестандартным способом.
Я собираюсь пойти против комментариев и проголосовать за __super. Это красиво и просто, и для 99,9% кода, который является SANE и не использует множественное наследование, с этим нет проблем. Явное указание базового класса привело к множеству трудно диагностируемых ужасных ошибок в нашей кодовой базе на протяжении многих лет, когда люди в конечном итоге случайно переходят прямо с Level3-> Level1 и пропускают класс Level2 посередине; __super предотвращает это. Также переносимый код, это хороший момент, однако не весь код заботится о переносимости.
@OrionEdwards именно для этого кода с одним наследованием __super не добавляет функциональности, потому что вы можете явно назвать этот класс (или получить привычку делать typedef Parent super сразу после объявления класса, если вы хотите написать super): его основная цель - отправить вызов функции одного из двух суперклассов, когда метод может быть в том или ином (например, сценарий микширования). Жертва соответствия стандартам может быть оправдана, если вам явно нужна эта функциональность, а не из-за лени.
@OrionEdwards, хорошо, всегда вызывайте методы Level2 из Level3, и все будет в порядке
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();
}
Справочный пример.
Не могли бы вы объяснить редактировать, почему / как этот код отвечает на вопрос? Ответы, состоящие только из кода, не приветствуются, потому что на них не так легко научиться, как на коде с объяснением. Без объяснения требуется значительно больше времени и усилий, чтобы понять, что было сделано, какие изменения были внесены в код или полезен ли код. Объяснение важно как для людей, пытающихся извлечь уроки из ответа, так и для тех, кто оценивает ответ, чтобы увидеть, действителен ли он или стоит ли голосовать.
Этот ответ касается вложенных классов, в то время как вопрос касался производных классов (хотя слова «родительский» и «дочерний» немного ошибочны) и, следовательно, не отвечает на вопрос вообще.
Если модификатор доступа функции-члена базового класса защищен ИЛИ общедоступен, вы можете вызвать функцию-член базового класса из производного класса. Можно вызвать невиртуальную и виртуальную функцию-член базового класса из производной функции-члена. Пожалуйста, обратитесь к программе.
#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
Спасибо, что принесли несколько примеров с virtual!
Вызовите родительский метод с оператором разрешения родительской области.
Parent::method()
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
}
};
Все приведенные выше решения предполагают, что ваша функция печати является статическим методом. Так ли это? Если метод не является статическим, то приведенные выше решения не актуальны.