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

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

  • автор:

Вызов функции одного класса внутри функции другого класса

Как передать функцию, возвращающую некоторую величину из одного класса в качестве переменной в функции, определенной в другом классе. На данный момент мой код выглядит примерно так:

При попытке вызова функции min() в decay(), min всегда равен нулю.

Kromster's user avatar

По указателю, очевидно.

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

У вас проблема скорее в вычислении min , а не в передаче функции. Вернее, не в передаче, а вызове. Вы не делаете ничего сверхъестественного. Код ниже — упрощенная версия вашего. Но что такое у вас dtim , какие там значения и т.д. и т.п. — этого же мы не знаем! Потом, у вас в decay создается объект B конструктором по умолчанию — так и надо? Может, в этом b все элементы dtim — нулевые?

Другое дело, если вам действительно надо передать функцию-член одного класса в другую.

Harry's user avatar

Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.8.29.43607

Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.

Вызов функции из класса

как исправить?как заставить увидеть ее?что не так делаю?

Вызов функции из класса
Доброго времени суток, есть вот такой код, в котором я пытаюсь вызвать функцию f в main из другого.

Вызов функции вне класса
Есть класс, в нем два метода. В одном из них надо вызвать функцию, которая расположена вне класса.

Вызов функции класса по каллбеку
Хочу реализовать возможность вызова метода класса по каллбеку. Сделал такой класс: class ICallable.

Вызов функции по указателю из класса
Такой расклад. Допустим имеем код: #include <iostream> using namespace std; template <class.

мож и элементарно. но спрашиваю потому что знать хочу и надо. опыта нет у меня в программировании и спросить не у кого. За подсказки огромное Спасибо!

Добавлено через 11 часов 4 минуты 17 секунд
а параметры функции?

Объявите и функцию и класс в хедере.

Добавлено через 4 минуты 7 секунд
Сделай так, чтобы бы было похоже на это:

Сообщение от stolyars

нужно ли передавать в качестве параметров в эту функцию объекты классов Календарь и Телефонной книги?Или можно сделать чтоб без параметров работала?
Простите зарагнее за назойливость ,может через года 2 для меня это тоже будет элементарно но а пока не получается у меня с этим. прошу вашей помощи.

пробовал в хидере сделать функцию glav_menu требует включения в этом хидере инклудов иостреам и других. так что еще один Майн получается..

17.7 – Вызов унаследованных функций и переопределение поведения

По умолчанию производные классы наследуют все поведения, определенные в базовом классе. В этом уроке мы более подробно рассмотрим, как выбираются функции-члены, а также как мы можем использовать это для изменения поведения в производном классе.

Вызов функции базового класса

Когда функция-член вызывается с объектом производного класса, компилятор сначала проверяет, существует ли этот член в производном классе. Если нет, он начинает переходить по цепочке наследования и проверять, определен ли член в каком-либо из родительских классов. И использует первый найденный.

Рассмотрим следующий пример:

Эта программа печатает

Когда вызывается derived.identify() , компилятор проверяет, определена ли функция identify() в классе Derived . Ее там нет. Затем он начинает искать наследуемые классы (в данном случае это Base ). Base определил функцию identify() , поэтому компилятор использует ее. Другими словами, Base::identify() использовалась, потому что Derived::identify() не существует.

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

Переопределение поведения

Однако если бы мы определили Derived::identify() в классе Derived , то использовалась бы эта функция.

Это означает, что мы можем заставить функции работать с нашими производными классами по-другому, переопределив их в производных классах!

В нашем примере выше было бы более правильно, если бы derived.identify() напечатала " I am a Derived ". Давайте изменим функцию identify() в классе Derived , чтобы она возвращала правильный ответ, когда мы вызываем identify() с объектом Derived .

Чтобы изменить в производном классе способ работы функции, определенной в базовом классе, просто переопределите эту функцию в производном классе.

Вот тот же пример, что и выше, с использованием новой функции Derived::identify() :

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

Добавление дополнений к существующей функциональности

Иногда мы не хотим полностью заменять функцию базового класса, а вместо этого хотим добавить к ней дополнительный функционал. Обратите внимание, что в приведенном выше примере Derived::identify() полностью скрывает Base::identify() ! Возможно, это не то, что нам нужно. Допускается, чтобы наша производная функция вызывала базовую версию функции с тем же именем (для повторного использования кода), а затем добавляла к ней дополнительный функционал.

Чтобы производная функция вызывала базовую функцию с тем же именем, просто выполните обычный вызов функции, но добавив перед именем функции квалификатор области видимости (имя базового класса и два двоеточия). В следующем примере переопределяется Derived::identify() , поэтому она сначала вызывает Base::identify() , а затем выполняет свои собственные дополнительные действия.

Теперь рассмотрим следующий пример:

Когда выполняется derived.identify() , вызов преобразуется в Derived::identify() . Однако первое, что делает Derived::identify() , – это вызывает Base::identify() , который выводит " I am a Base ". Когда Base::identify() возвращает управление, Derived::identify() продолжает выполнение и печатает " I am a Derived ".

Это должно быть довольно просто. Почему нам нужно использовать оператор разрешения области видимости ( :: )? Если бы мы определили Derived::identify() следующим образом:

Вызов функции identify() без квалификатора разрешения области видимости по умолчанию будет использовать identify() в текущем классе, которой будет Derived::identify() . Это приведет к тому, что Derived::identify() вызовет себя, что приведет к бесконечному циклу!

Есть одна хитрость, с которой мы можем столкнуться при попытке вызвать дружественные функции в базовых классах, такие как operator<< . Поскольку дружественные функции базового класса фактически не являются частью базового класса, использование квалификатора разрешения области видимости не сработает. Вместо этого нам нужен способ временно сделать так, чтобы наш класс Derived выглядел как Base , чтобы можно было вызвать правильную версию функции.

К счастью, это легко сделать с помощью static_cast . Например:

Поскольку Derived «является» Base , мы можем выполнить статическое приведение нашего объекта Derived в Base , чтобы вызывалась соответствующая версия operator<< , которая использует Base .

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

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

Содержание

Методы

Методы — это функции, объявление которых размещено внутри определения класса или структуры. В список переменных, доступных для метода, неявно попадают все поля структуры или класса, в котором он объявлен. Другими словами, в список областей видимости метода попадает область видимости структуры.

Взгляните на пример:

Методу Vec2f::getLength доступны все символы (т.е. переменные, функции, типы данных), которые были объявлены в одной из трёх областей видимости. При наличии символов с одинаковыми идентификаторами один символ перекрывает другой, т.к. поиск происходит от внутренней области видимости к внешней.

Понять идею проще на схеме. В ней область видимости названа по-английски: scope.

Схема

Поднимаясь по схеме от внутренней области видимости к внешней, легко понять, какие имена символов доступны в методе getLength:

  1. локальная переменная “lengthSquare”
  2. поля Vec2f под именами “x” и “y”
  3. всё, что есть в глобальной области видимости

К слову, в других методах структуры Vec2f переменная “lengthSquare” будет недоступна, а поля “x” и “y” будут доступны.

Конструкторы

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

Посмотрите на простой пример. В нём есть проблема: и поля, и параметры конструктора названы одинаково. В результате в области видимости конструктора доступны только параметры, и своими именами они перекрывают поля!

Язык C++ предлагает два решения. Первый способ — использовать косвенное обращение к полям через привязанный к методу объект. Указатель на него доступен по ключевому слову this :

Второй путь считается более правильным: мы используем специальную возможность конструкторов — “списки инициализации конструктора” (англ. constructor initializer lists). Списки инициализации — это список, разделённый запятыми и начинающийся с “:”. Элемент списка инициализации выглядит как field(expression) , т.е. для каждого выбранного программистом поля можно указать выражение, инициализирующее его. Имя переменной является выражением. Поэтому мы инициализируем поле его параметром:

Объявление и определение методов

C++ требует, чтобы каждый метод структуры или класса был упомянут в определении этой структуры или класса. Но допускается писать лишь объявление метода, о определение размещать где-нибудь в другом месте:

Классы и структуры

В C++ есть ключевое слово class — это практически аналог ключевого слова struct . Оба ключевых слова объявляют тип данных, и разница между ними есть только на стыке наследования и инкапсуляции. Других различий class и struct не существует.

Основы инкапсуляции

В C++ можно блокировать доступ к полям извне, но сохранять доступ для методов. Для этого введены три области доступа

  1. public — символ в этой области доступен извне
  2. private — символ из этой области доступен лишь собственных в методах
  3. protected — используется редко, о нём можете прочитать в документации

Давайте сделаем поля типа Vec2f недоступными извне. Также мы заменим ключевое слово struct на class — это не меняет смысла программы, но считается хорошим тоном использовать struct только если все поля доступны публично.

Запомните несколько хороших правил:

  • Используйте struct, если все поля публичные и не зависят друг от друга; используйте class, если между полями должны соблюдаться закономерности (например, поле “площадь” круга должно быть)

Основы наследования

В C++ новый тип может наследовать все поля и методы другого типа. Для этого достаточно указать структуру или класс в списке базовых типов. Такой приём используется в SFML при объявлении классов фигур:

Что означает public перед именем базового типа? Во-первых внешний код может передать RectangleShape в функцию, принимающую ссылку на Shape, то есть возможен так называемы upcast от более низкого (и более конкретного) типа RectangleShape к более высокому (и более абстрактному) типу Shape:

Во-вторых из-за public наследования все унаследованные поля и методы сохраняют свой уровень доступ: приватные остаются приватными, публичные остаются публичными. А если бы мы наследовали Shape с ключевым словом private, то уровень доступа стал бы ниже: все методы и поля стали бы приватными:

Контроль уровня доступа полей и методов — хитрый механизм, пройдёт немало времени, прежде чем вы научитесь пользоваться им правильно. В начале просто старайтесь сделать правильный выбор между private и public. Скорее всего поля будут private, а конструктор и все методы будут public. Это позволяет сохранять инвариант класса, то есть держать поля объекта в согласованном состоянии независимо от того, какие методы вызывают извне.

Основы полиморфизма: виртуальные методы и их перегрузка

SFML использует ещё одну идиому C++: виртуальные методы. Ключевые слова virtual , final , override относятся именно к этой идиоме. Например, в SFML определяется класс Drawable, который обозначает “сущность, которую можно нарисовать”. Все рисуемые классы SFML, включая sf::Sprite , sf::RectangleShape , sf::Text , прямо или косвенно наследуются от sf::Drawable .

Зачем это надо? Дело в том, что метод draw класса RenderWindow принимает параметр типа Drawable . Тем не менее, этот метод успешно рисует любые типы объектов: спрайты, фигуры, тексты. Он не выполняет проверок — он просто настраивает состояние рисования (RenderStates) и вызывает метод draw у сущности, которая является Drawable .

Виртуальный метод вызывается косвенно: если класс Shape , унаследованный от Drawable , переопределил метод, а потом был передан как параметр типа Drawable , то вызов метода draw всё равно приведёт к вызову переопределённого метода Shape::draw , а не метода Drawable::draw ! С обычными (не виртуальными) методами такого не происходит: если бы мы убрали слово virtual из объявления draw , то вызов метода draw у параметра типа Drawable всегда приводил бы к вызову Drawable::draw , даже если реальный тип объекта, скрытого за этим параметром, совсем другой.

Возможность по-разному реагировать на одинаковый вызов метода извне называется полиморфизмом. Точнее, это одна из разновидностей полиморфизма объектов.

Другими словами, RenderWindow и RectangleShape не знают, что они работают друг с другом, но тем не менее каждый вызывает правильный метод другого класса!

Иллюстрация

Когда вы просто вызываете window.draw(shape) , повышение класса происходит дважды: сначала конкретный класс фигуры повышается до более ограниченного класса Drawable, затем конкретный класс RenderWindow повышается до абстрактного RenderTarget. Всё это не требует времени при выполнении: просто компилятор выполняет проверки типов данных ещё при компиляции, не более того.

Как унаследовать Drawable: практический пример

Мы создадим класс, который рисует флаг России. Он будет унаследован от Drawable, чтобы использовать для рисования обычный метод draw у объекта окна.

Теперь мы можем реализовать конструктор и метод draw. В конструкторе мы должны вычислить и установить позиции и размеры трёх полос на флаге, а в методе draw мы должны их последовательно нарисовать.

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

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