Классы в 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:
- локальная переменная “lengthSquare”
- поля Vec2f под именами “x” и “y”
- всё, что есть в глобальной области видимости
К слову, в других методах структуры 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++ можно блокировать доступ к полям извне, но сохранять доступ для методов. Для этого введены три области доступа
- public — символ в этой области доступен извне
- private — символ из этой области доступен лишь собственных в методах
- 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; }