Как вызвать функцию из класса c
Перейти к содержимому

Как вызвать функцию из класса c

[C++] Как из метода прозводного класса вызвать метод базового класса с тем же именем?

Что-то не могу понять, можно ли такое провернуть в C++.

Есть базовый класс, и производный от него. В базовом классе есть некий метод (не чисто виртуальный, а с кодом). Пускай он называется action() к примеру.

Метод action() переопределяется в производном классе. И вот стоит задача в методе action() производного класса вызвать метод action() базового класса.

То есть нужно сделать так, чтобы действия, выполняемые в производном классе, не «заменяли» действия базового класса, а «дополняли».

Вопрос — возможно ли так сделать в C++? Если возможно, то как?

Вызов методов базового класса

Надо вызвать метод базового класса из метода, который переопределен в производном классе.

Из конструктора дочернего класса нужно явно вызывать конструктор родительского класса.

Обращение к базовому классу происходит с помощью super()

Нужно явно вызывать конструктор базового класса

class A(object): def __init__(self, x=5): print('A.__init__') self.x = x class B(A): def __init__(self, y=2): print('B.__init__') self.y = y k = B(7) # B.__init__ print('k.y =', k.y) # k.y = 7 #print('k.x =', k.x) # AttributeError: 'B' object has no attribute 'x' 

Видно, что без явного вызова конструктора класса А не вызывается A.__init__ и не создается поле x класса А.

Вызовем конструктор явно.

Конструктор базового класса стоит вызывать раньше, чем иницилизировать поля класса-наследника, потому что поля наследника могут зависеть (быть сделаны из) полей экземпляра базового класса.

class A(object): def __init__(self, x=5): print('A.__init__') self.x = x class B(A): def __init__(self, y=2): print('B.__init__') super().__init__(y/2) self.y = y k = B(7) # B.__init__ # A.__init__ print('k.y =', k.y) # k.y = 7 print('k.x =', k.x) # k.x = 3.5 

super() или прямое обращение к классу?

Метод класса можно вызвать, используя синтаксис вызова через имя класса:

class Base(object): def __init__(self): print('Base.__init__') class A(Base): def __init__(self): Base.__init__(self) print('A.__init__') k = A() # Base.__init__ # A.__init__ 

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

class Base(object): def __init__(self): print('Base.__init__') class A(Base): def __init__(self): Base.__init__(self) print('A.__init__') class B(Base): def __init__(self): Base.__init__(self) print('B.__init__') class C(A, B): def __init__(self): A.__init__(self) B.__init__(self) print('C.__init__') x = C() # Base.__init__ # A.__init__ # Base.__init__ - второй вызов # B.__init__ # C.__init__ 

Видно, что конструктор Base.__init__ вызывается дважды. Иногда это недопустимо (считаем количество созданных экземпляров класса, увеличивая в конструкторе счетчик на 1; выдаем очередное auto id какому-то нашему объекту, например, номер пропуска или паспорта или номер заказа).

То же самое через super():

  • вызов конструктора Base.__init__ происходит только 1 раз.
  • вызваны конструкторы всех базовых классов.
  • порядок вызова конструкторов для классов А и В не определен.
class Base(object): def __init__(self): print('Base.__init__') class A(Base): def __init__(self): super().__init__() print('A.__init__') class B(Base): def __init__(self): super().__init__() print('B.__init__') class C(A, B): def __init__(self): super().__init__() print('C.__init__') x = C() # Base.__init__ # B.__init__ - вызваны конструкторы обоих базовых классов # A.__init__ - порядок вызова # C.__init__ 

Как это работает?

Для реализации наследования питон ищет вызванный атрибут начиная с первого класса до последнего. Этот список создается слиянием (merge sort) списков базовых классов:

  • дети проверяются раньше родителей.
  • если родителей несколько, то проверяем в том порядке, в котором они перечислены.
  • если подходят несколько классов, то выбираем первого родителя.

При вызове super() продолжается поиск, начиная со следующего имени в MRO. Пока каждый переопределенный метод вызывает super() и вызывает его только один раз, будет перебран весь список MRO и каждый метод будет вызван только один раз.

Не забываем вызывать метод суперкласса

А если где-то не вызван метод суперкласса?

class Base(object): def __init__(self): print('Base.__init__') class A(Base): def __init__(self): #super().__init__() - НЕ вызываем super() print('A.__init__') class B(Base): def __init__(self): super().__init__() print('B.__init__') class C(A, B): def __init__(self): super().__init__() print('C.__init__') x = C() # A.__init__ # C.__init__ print(C.__mro__) # (, , , , ) 

Заметим, что хотя в B.__init__ есть вызов super(), то до вызова B.__init__ не доходит.

  • Вызываем у объекта класса С метод __init__.
  • Ищем его в mro и находим С.__init__. Выполняем его.
  • В этом методе вызов super() — ищем метод __init__ далее по списку от найденного.
  • Находим A.__init__. Выполняем его. В нем нет никаких super() — дальнейший поиск по mro прекращается.

Нет метода в своем базовом классе, есть у родителя моего сиблинга

Определим класс, который пытается вызвать метод, которого нет в базовом классе:

class A(object): def spam(self): print('A.spam') super().spam() x = A() x.spam() 

получим, как и ожидалось:

A.spam Traceback (most recent call last): File "examples/oop_super_3.py", line 7, in x.spam() File "examples/oop_super_3.py", line 4, in spam super().spam() AttributeError: 'super' object has no attribute 'spam' 

Определим метод spam в классе В. Класс С, наследник А и В, вызывает метод A.spam(), который вызывает B.spam — класс В не связан с классом А.

class A(object): def spam(self): print('A.spam') super().spam() class B(object): def spam(self): print('B.spam') class C(A, B): pass y = C() y.spam() print(C.__mro__) 
A.spam B.spam (, , , ) 

Для объекта класса С вызвали метод spam(). Ищем его в MRO. Находим A.spam() и вызываем. Далее для super() из A.spam() идем дальше от найденного по списку mro и находим B.spam().

Отметим, что при другом порядке описания родителей class C(B, A) , вызывается метод B.spam() у которого нет super():

B.spam (, , , ) 

Вызываем метод spam для объекта класса С. В С его нет, ищем дальше в В. Находим. Вызваем. Далее super() нет и дальнейший поиск не производится.

Чтобы не было таких сюрпризов при переопределении методов придерживайтесь правил:

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

Обращение к дедушке

Игнорируем родителя

Если у нас есть 3 одинаковых метода foo(self) в наследуемых классах А, В(А), C(B), и нужно из C.foo() вызвать сразу A.foo() минуя B.foo(), то наши классы неправильно сконструированы (почему нужно игнорировать В? может, нужно было наследовать С от А, а не от В?). Нужен рефакторинг.

Но можно всегда вызвать метод по имени класса:

class A(object): def spam(self): print('A.spam') class B(A): def spam(self): print('B.spam') class C(B): def spam(self): A.spam(self) print('C.spam') y = C() y.spam() print(C.__mro__) 
A.spam C.spam (, , , ) 

Метод определен только у дедушки

Если в В такого метода нет, и из C.foo() нужно вызвать A.foo() (или в базовом классе выше по иерархии), вызываем super().foo() и больше не думаем, у какого пра-пра-пра-дедушки реализован этот метод.

Просто воспользуйтесь super() для поиска по mro.

class A(object): def spam(self): print('A.spam') class B(A): pass class C(B): def spam(self): super().spam() print('C.spam') y = C() y.spam() print(C.__mro__) 
A.spam C.spam (, , , ) 

super().super() не работает

Или мы ищем какого-то родителя в mro, или точно указываем из какого класса нужно вызвать метод.

Литература

  • Документация по python
  • Python’s super() considered super!
  • Python Cookbook, chapter 8.7 Calling a Method on a Parent Class

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

Подскажите пожалуйста, каким образом, можно в C++ сохранить функцию класса в переменную, и затем вызвать её из основного тела программы? Если делать всё просто в main, без разделение на классы, то всё работает. Я же хочу сделать именно в разных файлах.
Как бы я не пытался придумать, у меня выходит ошибка:

non-standard syntax; use '&' to create a pointer to member 

Пробую как-то так:

//Class.h: class Class < public: typedef void(Class::*funcType)(); funcType func; void test(); >; //Class.cpp: #include "Class.h" #include Class::Class() < func = Class::test; >void Class::test() < std::cout //Program.cpp #include #include "Class.h" void mian()

Отслеживать
задан 5 июн 2019 в 11:21
3 1 1 бронзовый знак

К тому же указатель на метод немного отличается от указателя на функцию. Ему нужен объект, к которому этот указатель на метод мы можем применить. И тогда получится такая штука (c.*c.func)();

5 июн 2019 в 11:49

Пробовал ставить * и & в различных комбинациях, это не изменяло ошибку. Что-то не пойму, как использовать (c.*c.func)

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

Adamos

Очень не советую использовать callback-и (да и большую часть других плюшек С++ последних стандартов) раньше, чем научитесь обходиться без них. Передавайте объекты, используйте более традиционные методы. Когда сможете написать все без callback-ов и потратите на отладку этого кода меньше, чем на написание - тогда можно смело с ними знакомиться и использовать. Начнете с них - превратите свой код в минное поле и не сможете понять, где рвануло и почему.

Решения вопроса 2

MrNexeon

Это называется функция обратного вызова, или Сallback

Существует достаточно много способов для реализации коллбеков, но самым современным решением в C++ является функтор.

Функтор, или функциональный объект - это просто класс с перезагруженным оператором () . Стандартная библиотека уже включает в себя интерфейс для удобного создания и использования функторов - std::function

#include void deleteAllItems(int); // foo; // 

Ответ написан более трёх лет назад
Нравится 2 2 комментария
Евгений Шатунов @MarkusD Куратор тега C++

Pavel Tananykhin , это ответ на твой вопрос. 🙂

Так же советую использовать библиотеку libsigc++, которая по своей сути является отщеплением и развитием Boost signals. Она простая в освоении, удобная в использовании и быстрая в работе.
libsigc.sourceforge.net/index.shtml
https://developer.gnome.org/libsigc++-tutorial/stable/

Евгений Шатунов @MarkusD Куратор тега C++

А. кстати. Еще неплохо будет добавить пример использования функторов для вызова this-call функций.

Пример использования.
cpp.sh/2fuxm

"I'm here to consult you" © Dogbert

В другом классе я создаю указатель для этой функции.
void(InventoryContainer::*fnDelete)(unsigned int);
Но при попытке вызвать эту функцию,
this->fnDelete(this->IFrom);
студия показывает мне что this в данном случае не корректен.

1. Как правильно вызвать функцию?

Правильно показывает. fnDelete должен быть вызван для объекта класса InventoryContainer, и синтаксис разыменования указателя на функцию-член малость другой:

InventoryContainer *that; . (that->*fnDelete)(IFrom);

2. Где то на StackOverflow видел,что для вызова такой функции нужен ещё объект.Не лучше ли тогда просто передать ссылку на объект и через него и вызвать функцию?

Смысл указателя на функцию-член в том, что он даёт возможность вызывать любую функцию с такой сигнатурой в данном объекте. Т.е. для динамического выбора:

class A < public: void f1(); void f2(); >; void (A::*f)(); if (. ) f = &A::f1; else f = &A::f2; . A *pa; . (pa->*f)(); // вызывает f1 или f2

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *