Перейти к содержимому

Как обратиться к методу класса с

  • автор:

Классы в C++ — урок 10

Весь реальный мир состоит из объектов. Города состоят из районов, в каждом районе есть свои названия улиц, на каждой улице находятся жилые дома, которые также состоят из объектов.

Практически любой материальный предмет можно представить в виде совокупности объектов, из которых он состоит. Допустим, что нам нужно написать программу для учета успеваемости студентов. Можно представить группу студентов, как класс языка C++. Назовем его Students .

  • Основные понятия
  • Модификаторы доступа public и private
  • Программа учета успеваемости студентов
  • Отделение данных от логики
  • Создание объекта через указатель
  • Конструктор и деструктор класса

Основные понятия

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

У каждого студента есть имя — name и фамилия last_name . Также, у него есть промежуточные оценки за весь семестр. Эти оценки мы будем записывать в целочисленный массив из пяти элементов. После того, как все пять оценок будут проставлены, определим средний балл успеваемости студента за весь семестр — свойство average_ball .

Методы — это функции, которые могут выполнять какие-либо действия над данными (свойствами) класса. Добавим в наш класс функцию calculate_average_ball() , которая будет определять средний балл успеваемости ученика.

  • Методы класса — это его функции.
  • Свойства класса — его переменные.
 // считаем среднее арифметическое average_ball = sum / 5.0; > // Имя студента std::string name; // Фамилия std::string last_name; // Пять промежуточных оценок студента int scores[5]; private: // Итоговая оценка за семестр float average_ball; >; 

Функция calculate_average_ball() просто делит сумму всех промежуточных оценок на их количество.

Модификаторы доступа public и private

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

Закрытые данные класса размещаются после модификатора доступа private . Если отсутствует модификатор public , то все функции и переменные, по умолчанию являются закрытыми (как в первом примере).

Обычно, приватными делают все свойства класса, а публичными — его методы. Все действия с закрытыми свойствами класса реализуются через его методы. Рассмотрим следующий код.

 // Получение среднего балла float get_average_ball() < return average_ball; >std::string name; std::string last_name; int scores[5]; private: float average_ball; >; 

Мы не можем напрямую обращаться к закрытым данными класса. Работать с этими данными можно только посредством методов этого класса. В примере выше, мы используем функцию get_average_ball() для получения средней оценки студента, и set_average_ball() для выставления этой оценки.

Функция set_average_ball() принимает средний балл в качестве параметра и присваивает его значение закрытой переменной average_ball . Функция get_average_ball() просто возвращает значение этой переменной.

Программа учета успеваемости студентов

Создадим программу, которая будет заниматься учетом успеваемости студентов в группе. Создайте заголовочный файл students.h, в котором будет находиться класс Students .

 class Students < public: // Установка имени студента void set_name(std::string student_name) < name = student_name; >// Получение имени студента std::string get_name() < return name; >// Установка фамилии студента void set_last_name(std::string student_last_name) < last_name = student_last_name; >// Получение фамилии студента std::string get_last_name() < return last_name; >// Установка промежуточных оценок void set_scores(int student_scores[]) < for (int i = 0; i < 5; ++i) < scores[i] = student_scores[i]; >> // Установка среднего балла void set_average_ball(float ball) < average_ball = ball; >// Получение среднего балла float get_average_ball() < return average_ball; >private: // Промежуточные оценки int scores[5]; // Средний балл float average_ball; // Имя std::string name; // Фамилия std::string last_name; >; 

Мы добавили в наш класс новые методы, а также сделали приватными все его свойства. Функция set_name() сохраняет имя студента в переменной name , а get_name() возвращает значение этой переменной. Принцип работы функций set_last_name() и get_last_name() аналогичен.

Функция set_scores() принимает массив с промежуточными оценками и сохраняет их в приватную переменную int scores[5] .

Теперь создайте файл main.cpp со следующим содержимым.

 #include "students.h" int main() < // Создание объекта класса Student Students student; std::string name; std::string last_name; // Ввод имени с клавиатуры std::cout > scores[i]; // суммирование sum += scores[i]; > // Сохраняем промежуточные оценки в объект класса Student student.set_scores(scores); // Считаем средний балл float average_ball = sum / 5.0; // Сохраняем средний балл в объект класса Students student.set_average_ball(average_ball); // Выводим данные по студенту std::cout

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

Объект класса Students характеризует конкретного студента. Если мы захотим выставить оценки всем ученикам в группе, то будем создавать новый объект для каждого из них. Использование классов очень хорошо подходит для описания объектов реального мира.

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

Введенные данные мы передаем set-функциям, которые присваивают их закрытым переменным класса. После того, как были введены промежуточные оценки, мы высчитываем средний балл на основе этих оценок, а затем сохраняем это значение в закрытом свойстве average_ball , с помощью функции set_average_ball() .

Скомпилируйте и запустите программу.

Отделение данных от логики

Вынесем реализацию всех методов класса в отдельный файл students.cpp.

 #include "students.h" // Установка имени студента void Students::set_name(std::string student_name) < Students::name = student_name; >// Получение имени студента std::string Students::get_name() < return Students::name; >// Установка фамилии студента void Students::set_last_name(std::string student_last_name) < Students::last_name = student_last_name; >// Получение фамилии студента std::string Students::get_last_name() < return Students::last_name; >// Установка промежуточных оценок void Students::set_scores(int scores[]) < for (int i = 0; i < 5; ++i) < Students::scores[i] = scores[i]; >> // Установка среднего балла void Students::set_average_ball(float ball) < Students::average_ball = ball; >// Получение среднего балла float Students::get_average_ball()

А в заголовочном файле students.h оставим только прототипы этих методов.

 class Students < public: // Установка имени студента void set_name(std::string); // Получение имени студента std::string get_name(); // Установка фамилии студента void set_last_name(std::string); // Получение фамилии студента std::string get_last_name(); // Установка промежуточных оценок void set_scores(int []); // Установка среднего балла void set_average_ball(float); // Получение среднего балла float get_average_ball(); private: // Промежуточные оценки int scores[5]; // Средний балл float average_ball; // Имя std::string name; // Фамилия std::string last_name; >; 

Такой подход называется абстракцией данных — одного из фундаментальных принципов объектно-ориентированного программирования. К примеру, если кто-то другой захочет использовать наш класс в своем коде, ему не обязательно знать, как именно высчитывается средний балл. Он просто будет использовать функцию calculate_average_ball() из второго примера, не вникая в алгоритм ее работы.

Над крупными проектами обычно работает несколько программистов. Каждый из них занимается написанием определенной части продукта. В таких масштабах кода, одному человеку практически нереально запомнить, как работает каждая из внутренних функций проекта. В нашей программе, мы используем оператор потокового вывода cout , не задумываясь о том, как он реализован на низком уровне. Кроме того, отделение данных от логики является хорошим тоном программирования.

В начале обучения мы говорили о пространствах имен (namespaces). Каждый класс в C++ использует свое пространство имен. Это сделано для того, чтобы избежать конфликтов при именовании переменных и функций. В файле students.cpp мы используем оператор принадлежности :: перед именем каждой функции. Это делается для того, чтобы указать компилятору, что эти функции принадлежат классу Students .

Создание объекта через указатель

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

 #include "students.h" int main() < // Выделение памяти для объекта Students Students *student = new Students; std::string name; std::string last_name; // Ввод имени с клавиатуры std::cout set_name(name); student->set_last_name(last_name); // Оценки int scores[5]; // Сумма всех оценок int sum = 0; // Ввод промежуточных оценок for (int i = 0; i < 5; ++i) < std::cout > scores[i]; // суммирование sum += scores[i]; > // Сохраняем промежуточные оценки в объект класса Student student->set_scores(scores); // Считаем средний балл float average_ball = sum / 5.0; // Сохраняем средний балл в объект класса Students student->set_average_ball(average_ball); // Выводим данные по студенту std::cout get_name() get_last_name() get_average_ball() 

При создании статического объекта, для доступа к его методам и свойствам, используют операция прямого обращения — « . » (символ точки). Если же память для объекта выделяется посредством указателя, то для доступа к его методам и свойствам используется оператор косвенного обращения — « -> ».

Конструктор и деструктор класса

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

 > private: int scores[5]; >; int main() < // Передаем двойку в конструктор Students *student = new Students(2); return 0; >

Мы можем исправить двойки, если ученик будет хорошо себя вести, и вовремя сдавать домашние задания. А на «нет» и суда нет 🙂

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

 class Students < public: // Деструктор ~Students() < std::cout >; int main() < Students *student = new Students; // Уничтожение объекта delete student; return 0; >

Как обратиться к методу класса с

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

Методы

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

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

#include struct Vec2f  float x = 0; float y = 0; // Объявление метода с именем getLength // 1) метод - это функция, привязанная к объекту // 2) полное имя метода: "Vec2f::getLength" // Метод имеет квалификатор "const", потому что он не меняет // значения полей и не вызывает другие не-const методы. float getLength() const  const float lengthSquare = x * x + y * y; return std::sqrt(lengthSquare); > >; 

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

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

Схема

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

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

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

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

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

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

struct Vec2f  float x = 0; float y = 0; // Имя метода-конструктора совпадает с именем типа, возвращаемый тип отсутствует Vec2f(float x, float y)  // поля x, y перекрыты, что делать? > >; 

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

struct Vec2f  float x = 0; float y = 0; Vec2f(float x, float y)  // Обращаемся к полю через указатель this this->x = x; this->y = y; > >; 

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

struct Vec2f  float x = 0; float y = 0; Vec2f(float x, float y) : x(x) // Перекрытия имён нет, т.к. согласно синтаксису C++ , y(y) // перед скобками может стоять только имя поля.  > >; 

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

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

// Определение структуры Vec2f содержит // - объявление конструктора // - объявление метода getLength struct Vec2f  float x = 0; float y = 0; Vec2f(float x, float y); float getLength() const; >; // Определение конструктора (добавлен квалификатор "Vec2f::") Vec2f::Vec2f(float x, float y) : x(x) , y(y)  > // Определение метода getLength (добавлен квалификатор "Vec2f::") float Vec2f::getLength() const  const float lengthSquare = x * x + y * y; return std::sqrt(lengthSquare); > 

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

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

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

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

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

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

class Vec2f  public: // начало списка публичных методов и полей Vec2f(float x, float y) : x(x) , y(y)  > float getLength() const  // Здесь поля x/y доступны, т.к. это внутренний метод const float lengthSquare = x * x + y * y; return std::sqrt(lengthSquare); > private: // начало списка недоступных извне методов и полей float x = 0; float y = 0; >; // ! ОШИБКА КОМПИЛЯЦИИ ! // Поля x/y недоступны для внешней функции void printVector(const Vec2f& v)  std::cout  <"["  <v.x  <","  <v.y  <"]"; > 

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

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

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

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

// ! КОД НАМЕРЕННО УПРОЩЁН! // Класс RectangleShape наследует все поля и методы Shape, // но также имеет дополнительные поля и методы. // Финальный список полей и методов составляет компилятор при сборке программы. // Смысл наследования: "прямоугольник является фигурой". class RectangleShape : public Shape  public: // Конструктор, принимающий один необязательный аргумент RectangleShape(const Vector2f& size = Vector2f(0, 0)); // Метод с одним аргументом void setSize(const Vector2f& size); // . >; // Класс Shape наследует все поля и методы двух классов: Drawable и Transformable // Смысл наследования: "фигура является сущностью, которую можно нарисовать // и у которой можно задать позицию/масштаб/вращение" class Shape : public Drawable, public Transformable  public: // Виртуальный деструктор Shape. // Деструктор вызывается // 1) для локальной переменной - при выходе из области видимости локальной переменной // 2) для параметра, переданного по значению - при выходе из функции // 3) для временных объектов в выражении - при завершении инструкции, в которой находится выражение // 4) для объектов в динамической памяти - при освобождении памяти (например, через delete) // Деструкторы простых типов int, sf::Vector2f и т.д. не делают ничего. // Деструкторы сложных типов освобождают ресурсы (например, удаляют текстуру или вспомогательныю память) virtual ~Shape(); // Метод setTexture, принимает 2 параметра, из которых 1 - необязательный. void setTexture(const Texture* texture, bool resetRect = false); // . >; 

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

// Хотя параметр имеет тип Shape, мы можем передать тип RectangleShape, // потому что RectangleShape является Shape. // Аналогично мы можем передать CircleShape. void drawShape(sf::RenderWindow& window, const Shape& shape)  window.draw(shape); > // Параметр имеет тип RectangleShape, и передать Shape или CircleShape нельзя, // потому что ни Shape, ни CircleShape не являются RectangleShape. void drawRect(sf::RenderWindow& window, const RectangleShape& shape)  window.draw(shape); > 

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

// ! КОД НАМЕРЕННО УПРОЩЁН! // Все поля и методы Shape, даже публичные, стали приватными в RectangleShape class RectangleShape : private Shape  public: RectangleShape(const Vector2f& size = Vector2f(0, 0)); void setSize(const Vector2f& size); >; 

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

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

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

// ! КОД НАМЕРЕННО УПРОЩЁН! class Drawable  public: // Виртуальный деструктор. virtual ~Drawable() <> // Виртуальный метод draw virtual void draw(RenderTarget& target, RenderStates states) const; >; 

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

void RenderWindow::draw(const Drawable& drawable)  RenderStates states = . ; // как-то настраиваем состояние. drawable.draw(*this, states); > 

Виртуальный метод вызывается косвенно: если класс 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 у объекта окна.

#pragma once #include  // Класс RussianFlag рисует флаг России. // Наследуем его от sf::Drawable, чтобы можно было рисовать флаг проще: // window.draw(flag); class RussianFlag : public sf::Drawable  public: // Конструктор принимает два параметра: положение и размер флага. RussianFlag(const sf::Vector2f& position, const sf::Vector2f& size); private: // Метод draw вызывается окном при рисовании флага, // то есть window.draw(flag) косвенно привозит к вызову этого метода. void draw(sf::RenderTarget& target, sf::RenderStates states) const override; sf::RectangleShape m_whiteStrip; sf::RectangleShape m_blueStrip; sf::RectangleShape m_redStrip; >; 

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

#include "RussianFlag.h" RussianFlag::RussianFlag(const sf::Vector2f& position, const sf::Vector2f& size)  const sf::Vector2f stripSize =  size.x, size.y / 3.f >; m_whiteStrip.setSize(stripSize); m_whiteStrip.setPosition(position); m_whiteStrip.setFillColor(sf::Color(0xFF, 0xFF, 0xFF)); m_blueStrip.setSize(stripSize); m_blueStrip.setPosition(position + sf::Vector2f 0.f, stripSize.y >); m_blueStrip.setFillColor(sf::Color(0, 0, 0xFF)); m_redStrip.setSize(stripSize); m_redStrip.setPosition(position + sf::Vector2f 0.f, 2.f * stripSize.y >); m_redStrip.setFillColor(sf::Color(0xFF, 0, 0)); > void RussianFlag::draw(sf::RenderTarget& target, sf::RenderStates states) const  target.draw(m_whiteStrip, states); target.draw(m_blueStrip, states); target.draw(m_redStrip, states); > 

Теперь использовать класс RussianFlag извне очень легко!

#include "RussianFlag.h" #include #include // Функция создаёт окно определённого размера с определённым заголовком. void initWindow(sf::RenderWindow& window)  sf::VideoMode videoMode(800, 600); const std::string title = "Russian Flag + class derived from sf::Drawable"; sf::ContextSettings settings; settings.antialiasingLevel = 8; window.create(videoMode, title, sf::Style::Default, settings); > int main()  sf::RenderWindow window; initWindow(window); RussianFlag flag(100, 50>, 300, 150>); while (window.isOpen())  sf::Event event; while (window.pollEvent(event))  if (event.type == sf::Event::Closed)  window.close(); > > // Рисуем flag как обычную фигуру или спрайт: вызовом window.draw. window.clear(); window.draw(flag); window.display(); > > 

PS-Group

  • PS-Group
  • sshambir@gmail.com
  • ps-group
  • image/svg+xml sshambir

Как обратиться к методу класса js?

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

class StaticMethodExample  // объявляем статический метод, относящийся ко всему классу static staticMethod()  return 'Обращение к методу класса'; >; // объявляем "обычный" метод, относящийся только к объекту (экземпляру класса) nonStaticMethod()  return 'Обращение к методу объекта'; >; > 

Обращение к статическому методу производится через имя класса:

console.log(StaticMethodExample.staticMethod()); // => Обращение к методу класса 

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

console.log(StaticMethodExample.nonStaticMethod()); // => TypeError: StaticMethodExample.nonStaticMethod is not a function 

Чтобы обратиться к нестатическому методу, создадим объект (экземпляр класса) с помощью ключевого слова new :

const object = new StaticMethodExample(); console.log(object.nonStaticMethod()); // => Обращение к методу объекта 

Обратиться через имя объекта к статическому методу невозможно:

console.log(object.staticMethod()); // => TypeError: object.staticMethod is not a function 

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

Как обратиться к методу класса из внешней функции?

Подскажите, как сделать так, чтобы эти функции заработали?

DoChangeAge(Alexander, NewAge);
DoShowInfo(Alexander);

94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:

Не могу обратиться к методу класса в функции
Добрый день. При написании программы возникла проблема — в функции не могу обратиться к методу.

Как обратиться к методу текущего класса
Вообщем такая проблема. У нас есть класс в этом классе есть метод. И нам нужно в теле данного.

Как обратиться к методу из другого класса
Доброго времени суток. Продолжаю изучение C#, и вот я с вопросом. Имеются 2 класса в отдельных.

Как обратиться к методу из другого метода внутри одного и того же класса
Доброго времени суток. Продолжаю изучение C#, и вот моя третья тема (или четвертая, за сегодня) с.

1212 / 909 / 270
Регистрация: 25.07.2009
Сообщений: 4,361
Записей в блоге: 5

доступ к методам не через «.» случайно?

Добавлено через 26 секунд

ЦитатаСообщение от OVERPOWER8 Посмотреть сообщение

name->GetAge();
name.GetAge();
Регистрация: 29.11.2009
Сообщений: 224

ЦитатаСообщение от outoftime Посмотреть сообщение

доступ к методам не через «.» случайно?

Добавлено через 26 секунд

Так-то это так, но компилятору не нравится:
(16, 17 строки):
error: conversion from ‘Employee*’ to non-scalar type ‘Employee’ requested

1212 / 909 / 270
Регистрация: 25.07.2009
Сообщений: 4,361
Записей в блоге: 5

ЦитатаСообщение от OVERPOWER8 Посмотреть сообщение

void GetAge() const < return *age; >

немного о своем: как вы здесь возвращаете значение?

Добавлено через 10 минут

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
#include using namespace std; class Employee { private: int *age; public: Employee() { age = new int(27); } ~Employee() { delete age; } void SetAge(int NewAge) { *age = NewAge; } int GetAge() { return *age; } }; int DoShowInfo(Employee* name) { return name->GetAge(); } void DoChangeAge(Employee* name, int NewAge) { name->SetAge(NewAge); } int main() { int NewAge = 17; Employee* Alexander = new Employee(); DoChangeAge(Alexander, NewAge); cout  (DoShowInfo(Alexander))  endl; delete Alexander; }

Регистрация: 29.11.2009
Сообщений: 224

ЦитатаСообщение от outoftime Посмотреть сообщение

немного о своем: как вы здесь возвращаете значение?
void GetAge() const < return *age; >

Да очень просто:

int SomeAge = name.GetAge();

Спасибо, outoftime
P. S. приведенный код — это всего лишь 3% программы.

1212 / 909 / 270
Регистрация: 25.07.2009
Сообщений: 4,361
Записей в блоге: 5
давай всю, разберем)) + а где спасибо? не видно..

Эксперт С++

2347 / 1720 / 148
Регистрация: 06.03.2009
Сообщений: 3,675
OVERPOWER8, почему

ЦитатаСообщение от OVERPOWER8 Посмотреть сообщение

int *age;

?
Чем это обосновано? Почему не

int age;

?
И зачем нужны функции DoShowInfo, DoChangeAge, если сам класс предоставляет аналогичные операции?
1212 / 909 / 270
Регистрация: 25.07.2009
Сообщений: 4,361
Записей в блоге: 5

я старался сделать по минимуму правок, весь код мне не известен..
хотя я согласен, что это нелогично..

Эксперт С++

2347 / 1720 / 148
Регистрация: 06.03.2009
Сообщений: 3,675
outoftime, извини, вопрос адресован OVERPOWER8. Просто цитату случайно с твоего поста взял.
Регистрация: 29.11.2009
Сообщений: 224

>> CyBOSSeR
А почему бы и нет? (Какой вопрос, такой и ответ).

Т. к. объектов класса Employee будет много, и чтобы в любой момент можно было просмотреть информацию о них, нужно просто запустить DoShowInfo(Employee);

>> outoftime
Программа сама написана только на 10% (приблизительно).

Если интересно, вот 10% программы:

Employee.hpp:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
// Employee.hpp #include #include #include using namespace std; bool CheckInt(char* str) { int symbols = 0; int digits = 0; while(*str !=0) { symbols++; if(isdigit(*str++)) digits++; } if(symbols==digits) return true; else return false; } class Employee { private: int* age; int* YearsOfService; int* Salary; public: Employee(int initAge, int initYOS, int initSalary); ~Employee(); void SetAge(int NewAge) { *age = NewAge; } void SetSalary(int NewSalary) { *Salary = NewSalary; } void SetYOS(int NewYOS) { *YearsOfService = NewYOS; } int GetAge() const { return *age; } int GetSalary() const {return *Salary; } int GetYOS() const { return *YearsOfService; } }; Employee::Employee(int initAge=27, int initYOS=9, int initSalary=1000) { age = new int(initAge); Salary = new int(initSalary); YearsOfService = new int(initYOS); } Employee::~Employee() { delete Salary; delete YearsOfService; delete age; }

Employee.cpp:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
#include "Employee.hpp" void DoShowInfo(Employee* name) { cout  endl; cout  "Age: "  name->GetAge()  endl; cout  "Salary: "  name->GetSalary()  endl; cout  "Years of service: "  name->GetYOS()  endl; } void DoChangeInfo(Employee* name, What2Change) { void ShowMenu() { cout  "***** MENU: *****"  endl; cout  "1: Change age"  endl; cout  "2: Change salary"  endl; cout  "3: Change years of service"  endl; cout  "4: Redisplay menu"  endl; cout  "5: View infomation"  endl; cout  "6: Exit"  endl; } int SelectMenu()  } choice = atoi(input); return choice; } int main() { cout  endl; Employee* Power = new Employee(27, 5, 1000); int choice; for(;;) { ShowMenu(); choice = SelectMenu(); switch(choice) { case 1: int NewAge; cout  endl  "Enter new age:\n-> "; cin >> NewAge; Power->SetAge(NewAge); cout  endl; break; case 2: int NewSalary; cout  endl  "Enter new salary:\n-> "; cin >> NewSalary; Power->SetSalary(NewSalary); cout  endl; break; case 3: int NewYOS; cout  endl  "How much years?\n-> "; cin >> NewYOS; Power->SetYOS(NewYOS); cout  endl; break; case 4: cout  endl; continue; case 5: DoShowInfo(Power); cout  endl; break; break; case 6: cout  endl; cout  "Exiting. "  endl; return 0; default: cout  "Wrong input"  endl; } } delete Power; Power = 0; return 0; }

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

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

https://alkogolizm.vyvod-iz-zapoya-v-stacionare-samara11.ru/