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

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

  • автор:

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

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

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

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

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

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

Как обратиться к методу класса 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 

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

Классы в 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; >

Методы (Руководство по программированию на C#)

Метод — это блок кода, содержащий ряд инструкций. Программа инициирует выполнение инструкций, вызывая метод и указывая все аргументы, необходимые для этого метода. В C# все инструкции выполняются в контексте метода.

Метод Main является точкой входа для каждого приложения C# и вызывается общеязыковой средой выполнения (CLR) при запуске программы. В приложении, использующем инструкции верхнего уровня, метод Main создается компилятором и содержит все инструкции верхнего уровня.

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

Сигнатуры методов

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

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

Параметры метода заключаются в скобки и разделяются запятыми. Пустые скобки указывают, что параметры методу не требуются. Этот класс содержит четыре метода:

abstract class Motorcycle < // Anyone can call this. public void StartEngine() // Only derived classes can call this. protected void AddGas(int gallons) < /* Method statements here */ >// Derived classes can override the base class implementation. public virtual int Drive(int miles, int speed) < /* Method statements here */ return 1; >// Derived classes must implement this. public abstract double GetTopSpeed(); > 

Доступ к методу

Вызов метода в объекте аналогичен доступу к полю. После имени объекта добавьте точку, имя метода и круглые скобки. Аргументы перечисляются в этих скобках и разделяются запятыми. Таким образом, методы класса Motorcycle могут вызываться, как показано в следующем примере:

class TestMotorcycle : Motorcycle < public override double GetTopSpeed() < return 108.4; >static void Main() < TestMotorcycle moto = new TestMotorcycle(); moto.StartEngine(); moto.AddGas(15); moto.Drive(5, 20); double speed = moto.GetTopSpeed(); Console.WriteLine("My top speed is ", speed); > > 

Параметры и аргументы метода

Определение метода задает имена и типы всех необходимых параметров. Когда вызывающий код вызывает метод, он предоставляет конкретные значения, называемые аргументами, для каждого параметра. Аргументы должны быть совместимы с типом параметра, но имя аргумента (если есть), используемое в вызывающем коде, не обязательно должно совпадать с именем параметра, указанным в методе. Например:

public void Caller() < int numA = 4; // Call with an int variable. int productA = Square(numA); int numB = 32; // Call with another int variable. int productB = Square(numB); // Call with an integer literal. int productC = Square(12); // Call with an expression that evaluates to int. productC = Square(productA * 3); >int Square(int i) < // Store input argument in a local variable. int input = i; return input * input; >

Передача по ссылке и передача по значению

По умолчанию при передаче в метод экземпляра типа значения вместо самого этого экземпляра передается его копия. Поэтому изменения в аргументе не оказывают влияния на исходный экземпляр в вызывающем методе. Чтобы передать экземпляр типа значения по ссылке, используйте ключевое слово ref . Дополнительные сведения см. в разделе Передача параметров типа значения.

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

Ссылочный тип создается с помощью ключевого слова class , как показано в следующем примере.

public class SampleRefType

Теперь, если передать объект, основанный на этом типе, в метод, то будет передана ссылка на объект. В следующем примере объект типа SampleRefType передается в метод ModifyObject :

public static void TestRefType() < SampleRefType rt = new SampleRefType(); rt.value = 44; ModifyObject(rt); Console.WriteLine(rt.value); >static void ModifyObject(SampleRefType obj)

В этом примере, в сущности, делается то же, что и в предыдущем примере, — аргумент по значению передается в метод. Но поскольку здесь используется ссылочный тип, результат будет другим. В данном случае в методе ModifyObject изменено поле value параметра obj , а также изменено поле value аргумента, rt в методе TestRefType . В качестве выходных данных метод TestRefType отображает 33.

Дополнительные сведения о передаче ссылочных типов по ссылке и по значению см. в разделах Передача параметров ссылочного типа и Ссылочные типы.

Возвращаемые значения

Методы могут возвращать значение вызывающему объекту. Если тип возврата, указываемый перед именем метода, не void , этот метод может возвращать значение с помощью оператора return . Инструкция с ключевым словом return , за которым следует значение, соответствующее типу возврата, будет возвращать это значение объекту, вызвавшему метод.

Значение можно вернуть вызывающему объекту по значению или по ссылке. Значения возвращаются вызывающему объекту по ссылке, если ключевое слово ref используется в сигнатуре метода и указывается после каждого ключевого слова return . Например, следующая сигнатура метода и оператор return указывают, что метод возвращает переменную с именем estDistance вызывающему объекту по ссылке.

public ref double GetEstimatedDistance()

Ключевое слове return также останавливает выполнение метода. Если тип возврата — void , инструкцию return без значения по-прежнему можно использовать для завершения выполнения метода. Без ключевого слова return этот метод будет останавливать выполнение при достижении конца блока кода. Методы с типом возврата, отличным от void, должны использовать ключевое слово return для возврата значения. Например, в следующих двух методах ключевое слово return используется для возврата целочисленных значений.

class SimpleMath < public int AddTwoNumbers(int number1, int number2) < return number1 + number2; >public int SquareANumber(int number) < return number * number; >> 

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

int result = obj.AddTwoNumbers(1, 2); result = obj.SquareANumber(result); // The result is 9. Console.WriteLine(result); 
result = obj.SquareANumber(obj.AddTwoNumbers(1, 2)); // The result is 9. Console.WriteLine(result); 

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

Чтобы использовать значение, возвращаемое по ссылке из метода, необходимо объявить локальную ссылочную переменную, если планируется изменение значения. Например, если метод Planet.GetEstimatedDistance возвращает значение Double по ссылке, можно определить его как локальную ссылочную переменную с использованием кода следующего вида:

ref double distance = ref Planet.GetEstimatedDistance(); 

Возвращать многомерный массив из метода M , который изменяет содержимое массива, необязательно, если вызывающая функция передает массив в M . В целях оптимизации можно возвращать полученный массив из M или функциональный поток значений, однако это необязательно. Это связано с тем, что C# передает все ссылочные типы по значению, а значение ссылки на массив представляет собой указатель на массив. В методе M любые изменения содержимого массива отслеживаются любым кодом, имеющим ссылку на массив, как показано в приведенном ниже примере:

static void Main(string[] args) < int[,] matrix = new int[2, 2]; FillMatrix(matrix); // matrix is now full of -1 >public static void FillMatrix(int[,] matrix) < for (int i = 0; i < matrix.GetLength(0); i++) < for (int j = 0; j < matrix.GetLength(1); j++) < matrix[i, j] = -1; >> > 

Асинхронные методы

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

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

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

В следующем примере метод Main служит примером асинхронного метода с типом возврата Task. Он переходит к методу DoSomethingAsync и, поскольку он выражается в одной строке, он может опустить ключевые слова async и await . Поскольку DoSomethingAsync является асинхронным методом, задача для вызова DoSomethingAsync должна быть ожидаемой, как показывает следующая инструкция: await DoSomethingAsync(); .

class Program < static Task Main() =>DoSomethingAsync(); static async Task DoSomethingAsync() < TaskdelayTask = DelayAsync(); int result = await delayTask; // The previous two statements may be combined into // the following statement. //int result = await DelayAsync(); Console.WriteLine($"Result: "); > static async Task DelayAsync() < await Task.Delay(100); return 5; >> // Example output: // Result: 5 

Асинхронный метод не может объявить все параметры ref или out , но может вызывать методы, которые имеют такие параметры.

Определения текста выражений

Часто используются определения методов, которые просто немедленно возвращаются с результатом выражения или которые имеют единственную инструкцию в тексте метода. Для определения таких методов существует сокращенный синтаксис с использованием => :

public Point Move(int dx, int dy) => new Point(x + dx, y + dy); public void Print() => Console.WriteLine(First + " " + Last); // Works with operators, properties, and indexers too. public static Complex operator +(Complex a, Complex b) => a.Add(b); public string Name => First + " " + Last; public Customer this[long id] => store.LookupCustomer(id); 

Если метод возвращает void или является асинхронным методом, то текст метода должен быть выражением инструкции (так же, как при использовании лямбда-выражений). Свойства и индексаторы должны быть только для чтения, и вы не должны использовать ключевое слово get метода доступа.

Итераторы

Итератор выполняет настраиваемую итерацию по коллекции, например по списку или массиву. Итератор использует инструкцию yield return для возврата всех элементов по одному. При достижении инструкции yield return текущее расположение в коде запоминается. При следующем вызове итератора выполнение возобновляется с этого места.

Итератор вызывается из клиентского кода с помощью инструкции foreach .

Дополнительные сведения см. в разделе Итераторы.

Спецификация языка C#

Дополнительные сведения см. в спецификации языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.

См. также

  • Руководство по программированию на C#
  • Система типов C#
  • Модификаторы доступа
  • Статические классы и члены статических классов
  • Наследование
  • Абстрактные и запечатанные классы и члены классов
  • params
  • out
  • ref;
  • Параметры методов

Совместная работа с нами на GitHub

Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.

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

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