У вас вопросы?
У нас ответы:) SamZan.net

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

Работа добавлена на сайт samzan.net:

Поможем написать учебную работу

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

Предоплата всего

от 25%

Подписываем

договор

Выберите тип работы:

Скидка 25% при заказе до 28.12.2024

Вопрос 14

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

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

class Students {

   // Имя студента

   std::string name;

   // Фамилия

   std::string last_name;

   // Пять промежуточных оценок студента

   int scores[5];

   // Итоговая оценка за семестр

   float average_ball;

};

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

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

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

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

  1.  Методы класса — это его функции.
  2.  Свойства класса — его переменные.

class Students {

   public:

       // Функция, считающая средний балл

       void calculate_average_ball()

       {

           int sum = 0; // Сумма всех оценок

           for (int i = 0; i < 5; ++i) {

               sum += scores[i];

           }

           // считаем среднее арифметическое

           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, то все функции и переменные, по умолчанию являются закрытыми (как в первом примере).

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

class Students {

   public:

       // Установка среднего балла

       void set_average_ball(float ball)

       {

           average_ball = ball;

       }

       // Получение среднего балла

       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.

/* students.h */

#include <string>

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 со следующим содержимым.

/* main.cpp */

#include <iostream>

#include "students.h"

int main()

{

   // Создание объекта класса Student

   Students student;

   std::string name;

   std::string last_name;

   // Ввод имени с клавиатуры

   std::cout << "Name: ";

   getline(std::cin, name);

   // Ввод фамилии

   std::cout << "Last name: ";

   getline(std::cin, last_name);

   // Сохранение имени и фамилии в объект класса Students

   student.set_name(name);

   student.set_last_name(last_name);

   // Оценки

   int scores[5];

   // Сумма всех оценок

   int sum = 0;

   // Ввод промеждуточных оценок 

   for (int i = 0; i < 5; ++i) {

       std::cout << "Score " << i+1 << ": ";

       std::cin >> 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 << "Average ball for " << student.get_name() << " "

        << student.get_last_name() << " is "

        << student.get_average_ball() << std::endl;

   return 0;

}

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

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

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

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

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

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

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

/* students.cpp */

#include <string>

#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()

{

   return Students::average_ball;

}

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

/* students.h */

#pragma once /* Защита от двойного подключения заголовочного файла */

#include <string>

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.

/* main.cpp */

#include <iostream>

#include "students.h"

int main()

{

   // Выделение памяти для объекта Students

   Students *student = new Students;

   std::string name;

   std::string last_name;

   // Ввод имени с клавиатуры

   std::cout << "Name: ";

   getline(std::cin, name);

   // Ввод фамилии

   std::cout << "Last name: ";

   getline(std::cin, last_name);

   // Сохранение имени и фамилии в объект класса Students

   student->set_name(name);

   student->set_last_name(last_name);

   // Оценки

   int scores[5];

   // Сумма всех оценок

   int sum = 0;

   // Ввод промежуточных оценок 

   for (int i = 0; i < 5; ++i) {

       std::cout << "Score " << i+1 << ": ";

       std::cin >> 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 << "Average ball for " << student->get_name() << " "

        << student->get_last_name() << " is "

        << student->get_average_ball() << std::endl;

   // Удаление объекта student из памяти

   delete student;

   return 0;

}

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

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

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

class Students {

   public:

       // Конструктор класса Students

       Students(int default_score)

       {

           for (int i = 0; i < 5; ++i) {

               scores[i] = default_score;

           }

       }

   private:

       int scores[5];

};

int main()

{

   // Передаем двойку в конструктор

   Students *student = new Students(2);

   return 0;

}

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

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

#include <iostream>

class Students {

   public:

       // Деструктор

       ~Students()

       {

           std::cout << "Memory has been cleaned. Good bye." << std::endl;

       }

};

int main()

{

   Students *student = new Students;

   // Уничтожение объекта

   delete student;

   return 0;

}

Управление Доступом к Элементам Класса

Модификаторы уровня доступа определяют, могут ли другие классы использовать определенное поле или вызвать определенный метод. Есть два уровня управления доступом:

  1.  На верхнем уровне —public, или частный на пакет (никакой явный модификатор).
  2.  На задействованном уровне —public, private, protected, или частный на пакет (никакой явный модификатор).

class может быть объявлен с модификатором public, когда, что class видим ко всем классам всюду. Если у class нет никакого модификатора (значение по умолчанию, также известное как частный на пакет), это видимо только в пределах его собственного пакета (пакеты называют группами связанных классов — Вы узнаете о них в более позднем уроке.)

На задействованном уровне можно также использовать public модификатор или никакой модификатор (частный на пакет) так же, как с высокоуровневыми классами, и с тем же самым значением. Для элементов есть два дополнительных модификатора доступа: private и protectedprivate модификатор определяет, что к элементу можно только получить доступ в его собственном class. protected модификатор определяет, что к элементу может только получить доступ в пределах его собственного пакета (как с частным на пакет) и, кроме того, подкласс его class в другом пакете.

Следующая таблица показывает доступ к элементам, разрешенным каждым модификатором.

Уровни доступа

Модификатор

Класс

Пакет

Подкласс

Мир

public

Y

Y

Y

Y

protected

Y

Y

Y

N

никакой модификатор

Y

Y

N

N

private

Y

N

N

N

Первый столбец данных указывает, есть ли у самого class доступ к элементу, определенному уровнем доступа. Как можно видеть, у class всегда есть доступ к его собственным элементам. Второй столбец указывает, есть ли у классов в том же самом пакете как class (независимо от их происхождения) доступ к элементу. Третий столбец указывает, есть ли у подклассов class, объявленного вне этого пакета, доступ к элементу. Четвертый столбец указывает, есть ли у всех классов доступ к элементу.

Уровни доступа влияют на Вас двумя способами. Во-первых, когда Вы используете классы, которые прибывают из другого источника, такого как классы в платформе Java, уровни доступа определяют, какие элементы тех классов Ваши собственные классы могут использовать. Во-вторых, когда Вы пишете class, Вы должны решить, какой уровень доступа каждая задействованная переменная и каждый метод в Вашем class должны иметь.

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

Классы и Пакеты Примера, Используемого, чтобы Иллюстрировать Уровни доступа

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

Видимость

Модификатор

Альфа

Бета

Alphasub

Гамма

public

Y

Y

Y

Y

protected

Y

Y

Y

N

никакой модификатор

Y

Y

N

N

private

Y

N

N

N

Подсказки относительно Выбора Уровня доступа: 

Если другие программисты используют Ваш class, Вы хотите гарантировать, что ошибки от неправильного употребления не могут произойти. Уровни доступа могут помочь Вам сделать это.

  1.  Используйте самый рестриктивный уровень доступа, который имеет смысл для определенного элемента. Использовать private если у Вас нет серьезного основания не к.
  2.  Избежать public поля за исключением констант. (Многие из примеров в учебном руководстве используют общедоступные поля. Это может помочь проиллюстрировать некоторые тезисы кратко, но не рекомендуется для производственного кода.) Общедоступные поля имеют тенденцию соединять Вас с определенной реализацией и ограничивать Вашу гибкость в изменении Вашего кода.
  3.  Спецификаторы доступа к элементу public и private(а также, как мы увидим в главе 9 «Наследование»,protected — защищенные) используются для управления доступом к данным-элементам класса и функциям-элемен- там. По умолчанию режим доступа для классов — private (закрытый), так что все элементы после заголовка класса и до первого спецификатора доступа являются закрытыми. После каждого спецификатора режим доступа, определенный им, действует до следующего спецификатора или до завершающей правой скобки (}) определения класса. Спецификаторыprivatepublic и protected могут быть повторены, но такое употребление редко и может привести к беспорядку.
  4.  Закрытые элементы класса могут быть доступны только для функций- элементов (и дружественных функций) этого класса. Открытые элементы класса могут быть доступны для любых функций в программе.
  5.  Основная задача открытых элементов состоит в том, чтобы дать клиентам класса представление о возможностях (услугах), которые обеспечивает класс. Этот набор услуг составляет открытый интерфейскласса. Клиентов класса не должно касаться, каким образом класс выполняет их задачи. Закрытыеэлементы класса и описания открытых функций-элементов недоступны для клиентов класса. Эти компоненты составляют реализацию (implementation)класса.
  6.  Замечание по технике программирования 6.10
  7.  С++ способствует созданию программ, не зависящих от реализации. Если, изменяется реализация класса, используемого программой, не зависящей от реализации, то код этой программы не требует изменения, но может потребоваться его перекомпиляция.
  8.  Типичная ошибка программирования 6.4
  9.  Попытка с помощью функции, не являющейся элементом определенного класса (или другом этого класса) получить доступ к элементам этого класса.

Статистические элементы классов

До настоящего момента каждый создаваемый вами объект имел свой собственный набор элементов данных. В зависимости от назначения вашего приложения могут быть ситуации, когда объекты одного и того же класса должны совместно использовать один или несколько элементов данных. Например, предположим, что вы пишете программу платежей, которая отслеживает рабочее время для 1000 служащих. Для определения налоговой ставки программа должна знать условия, в которых работает каждый служащий. Пусть для этого используется переменная класса state_of_work. Однако, если все служащие работают в одинаковых условиях, ваша программа могла бы совместно использовать этот элемент данных для всех объектов типа employee. Таким образом, ваша программа уменьшает необходимое количество памяти, выбрасывая 999 копий одинаковой информации. Для совместного использования элемента класса вы должны объявить этот элемент как static (статический). Этот урок рассматривает шаги, которые вы должны выполнить для совместного использования элемента класса несколькими объектами. К концу этого урока вы освоите следующие основные концепции:

  1.  C++ позволяет иметь объекты одного и того же типа, которые совместно используют один или несколько элементов класса.
  2.  Если ваша программа присваивает значение совместно используемому элементу, то все объекты этого класса сразу же получают доступ к этому новому значению.
  3.  Для создания совместно используемого элемента данных класса вы должны предварять имя элемента класса ключевым словом static.
  4.  После того как программа объявила элемент класса как static, она должна объявить глобальную переменную (вне определения класса), которая соответствует этому совместно используемому элементу класса.
  5.  Ваши программы могут использовать ключевое слово static, чтобы сделать метод класса вызываемым в то время, как программа, возможно, еще не объявила каких-либо объектов данного класса.

СОВМЕСТНОЕ ИСПОЛЬЗОВАНИЕ ЭЛЕМЕНТА ДАННЫХ

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

private:
static int shared_value;

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

int class_name::shared_value;

Следующая программа SHARE_IT.CPP определяет класс book_series, совместно использующий элемент page_count, который является одинаковым для всех объектов (книг) класса (серии). Если программа изменяет значение этого элемента, изменение сразу же проявляется во всех объектах класса:

#include <iostream.h>

#include <string.h>

class book_series

{
public:
   book_series(char *, char *, float);
   void show_book(void);
   void set_pages(int) ;
private:
   static int page_count;
   char title[64];
   char author[ 64 ];
   float price;
};

int book_series::page__count;

void book_series::set_pages(int pages)

{
   page_count = pages;
}

book_series::book_series(char *title, char *author, float price)

{
   strcpy(book_series::title, title); 
   strcpy(book_series::author, author);
   book_series::price = price;
}

void book_series:: show_book (void)

{
   cout << "
Заголовок: " << title << endl;
   cout << "
Автор: " << author << endl;
   cout << "
Цена: " << price << endl;
   cout << "
Страницы: " << page_count << endl;
}

void main(void)

{
   book_series programming( "
Учимся программировать на C++", "Jamsa", 22.95);
   book_series word( "
Учимся работать с Word для Windows", "Wyatt", 19.95);
   word.set_pages(256);
   programming.show_book ();
   word.show_book() ;
   cout << endl << "
Изменение page_count " << endl;
   programming.set_pages(512);
   programming.show_book();
   word.show_book();
}

Как видите, класс объявляет page_count как static int. Сразу же за определением класса программа объявляет элемент page_count как глобальную переменную. Когда программа изменяет элемент page_count, изменение сразу же проявляется во всех объектах классаbook_series.

Совместное использование элементов класса

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

Использование элементов с атрибутами public static, если объекты не существуют

Как вы только что узнали, при объявлении элемента класса как static этот элемент совместно используется всеми объектами данного класса. Однако возможны ситуации, когда программа еще не создала объект, но ей необходимо использовать элемент. Для использования элемента ваша программа должна объявить его как public и static. Например, следующая программа USЕ_MBR.CPP использует элемент page_count из класса book_series,даже если объекты этого класса не существуют:

#include <iostream.h>

#include <string.h>

class book_series


public:
   static int page_count;
private:
   char title [64];
   char author[64];
   float price;
};

int book_series::page_count;

void main(void)
{
   book_series::page_count = 256;
   cout << "
Текущее значение page_count равно " << book_series::page_count << endl;
}

В данном случае, поскольку класс определяет элемент класса page_count как public,программа может обратиться к этому элементу класса, даже если объекты класса book_seriesне существуют.

ИСПОЛЬЗОВАНИЕ СТАТИЧЕСКИХ ФУНКЦИЙ-ЭЛЕМЕНТОВ

Предыдущая программа иллюстрировала использование статических элементов данных. Подобным образом C++ позволяет вам определить статические функции-элементы (методы). Если вы создаете статический метод, ваша программа может вызывать такой метод, даже если объекты не были созданы. Например, если класс содержит метод, который может быть использован для данных вне класса, вы могли бы сделать этот методстатическим. Ниже приведен класс menu, который использует esc-последовательность драйвера ANSI для очистки экрана дисплея. Если в вашей системе установлен драйвер ANSI.SYS, вы можете использовать метод clear_screen для очистки экрана. Поскольку этот метод объявлен как статический, программа может использовать его, даже если объекты типа menu не существуют. Следующая программа CLR_SCR.CPP использует методclear_screen для очистки экрана дисплея:

#include <iostream.h>

class menu


public:
   static void clear_screen(void); 
   //
Здесь должны быть другие методы 
private:
   int number_of_menu_options;
};

void menu::clear_screen(void)

{
   cout << "33" << "[2J";
}

void main(void)

{
   menu::clear_screen();
}

Так как программа объявляет элемент clear_screen как статический, она может использовать эту функцию для очистки экрана, даже если объекты типа menu не существуют. Функция clear_screen использует esc-последовательность ANSI Esc[2J для очистки экрана.

Использование в ваших программах методов класса

По мере создания методов класса возможны ситуации, когда функция, созданная вами для использования классом, может быть полезна для операций вашей программы, которые не включают объекты класса. Например, в классе menu была определена функция clear_screen, которую вы, возможно, захотите использовать в программе. Если ваш класс содержит метод, который вы захотите использовать вне объекта класса, поставьте перед его прототипом ключевое слово static и объявите этот метод как public:

public:
static void clear_screen(void);

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

  1.  Когда вы объявляете элемент класса как static, то такой элемент может совместно использоваться всеми объектами данного класса.
  2.  После того как ваша программа объявляет элемент класса как static, она должна вне определения класса объявить глобальную переменную, соответствующую совместно используемому элементу класса.
  3.  Если вы объявляете элемент как public и static, ваша программа может использовать такой элемент, даже если объекты данного класса не существуют. Для обращения к этому элементу ваша программа должна использовать оператор глобального разрешения, например class_name::member_name.
  4.  Если вы объявляете общую статическую функцию-элемент, ваша программа может вызывать эту функцию, даже если объекты данного класса не существуют. Для вызова данной функции программа должна использовать оператор глобального разрешения, например menu::clear_screen().

Стековые и динамические объекты

Иногда мне кажется, что C++ лучше изучать без предварительного знакомства с C. В C++ часто используются те же термины, что и в С, но за ними кроются совершенно иной смысл и правила применения. Например, возьмем примитивный целый тип.

int x = 17;

В C++ это будет экземпляр встроенного «класса» int. В С это будет... просто int. Встроенные классы имеют свои конструкторы. У класса int есть конструктор с одним аргументом, который инициализирует объект передаваемым значением. Теоретически существует и деструктор, хотя он ничего не делает и ликвидируется всеми нормальными разработчиками компиляторов в процессе оптимизации. Важно осознать, что встроенные типы за очень редкими исключениями подчиняются тем же базовым правилам, что и ваши расширенные типы.

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

Размещение в стеке

Чтобы выделить память для стековой переменной в области действия блока, достаточно просто объявить ее обычным образом.

{

int i;

foo f(constructor_args);

// Перед выходом из блока вызываются деструкторы i и f

}

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

{

int i;

foo f;

SomeFunction(&f);

}

Без изучения функции SomeFunction невозможно сказать, безопасен ли этот фрагмент.

SomeFunction может передать адрес дальше или сохранить его в какой-нибудь переменной, а по закону Мэрфи этот адрес наверняка будет использован уже после уничтожения объекта f. Даже если сверхтщательный анализ SomeFunction покажет, что адрес не сохраняется после вызова, через пару лет какой-нибудь новый программист модифицирует SomeFunction, продлит существование адреса на пару машинных команд и - БУМ!!! Лучше полностью исключить такую возможность и не передавать адреса стековых объектов.

Динамическое размещение

Чтобы выделить память для объекта в куче (heap), воспользуйтесь оператором new new.

foo* f = new foo(constructor_args);

Вроде бы все просто. Оператор new выделяет память и вызывает соответствующий конструктор на основании переданных аргументов. Но когда этот объект уничтожается? Подробный ответ на этот вопрос займет примерно треть книги, но я не буду вдаваться в технические детали и отвечу так: «Когда кто-нибудь вызовет оператор delete для его адреса». Сам по себе объект из памяти не удалится; вы должны явно сообщить своей программе, когда его следует уничтожить.

Указатели и ссылки

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

{

foo f;

foo* p = &f;

f.MemberFn(); // Использует сам объект

p->MemberFn(); // Использует его адрес

p = new foo;

foo& r = *p; // Ссылка на объект

r.MemberFn(); // То же, что и p->MemberFn()

}

Как видите, выбор оператора . или -> зависит от типа переменной и не имеет отношения к атрибутам самого объекта. Раз уж мы заговорили об этом, правильные названия этих операторов (. и ->) - селекторы членов класса (member selectors). Если вы назовете их «точкой» или «стрелкой» на семинаре с коктейлями, наступит гробовая тишина, все повернутся и презрительно посмотрят на вас, а в дальнем углу кто-нибудь выронит свой бокал.

Недостатки стековых объектов

Если использовать оператор delete для стекового объекта, то при большом везении ваша программа просто грохнется. А если вам (как и большинству из нас) не повезет, то программа начнет вести себя, как ревнивая любовница - она будет вытворять, всякие гадости в разных местах памяти, но не скажет, на что же она разозлилась. Дело в том, что в большинстве реализаций C++ оператор new записывает пару скрытых байтов перед возвращаемым адресом. В этих байтах указывается размер выделенного блока. По ним оператор delete определяет, сколько памяти за указанным адресом следует освободить.

При выделении памяти под стековые объекты оператор new не вызывается, поэтому эти

дополнительные данные отсутствуют. Если вызвать оператор delete для стекового объекта, он возьмет содержимое стека над вашей переменной и интерпретирует его как размер освобождаемого блока.

Итак, мы знаем по крайней мере две причины, по которым следует избегать стековых объектов - если у вас нет действительно веских доводов в их пользу:

1. Адрес стекового объекта может быть сохранен и использован после выхода за границы области действия объекта.

2. Адрес стекового объекта может быть передан оператору delete.

Следовательно, для стековых объектов действует хорошее правило: Никогда не получайте их адреса или адреса их членов.

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

Автоматическое удаление - второе большое преимущество стековых объектов, поэтому

программисты часто создают маленькие вспомогательные стековые классы, которые играют роль «обертки» для динамических объектов. В следующем забавном примере динамический класс Foo «упаковывается» в стековый класс PFoo. Конструктор выделяет память для Foo; деструктор освобождает ее. Если вы незнакомы с операторами преобразования, обратитесь к соответствующему разделу этой главы. В двух словах, функция operator Foo*() позволяет использовать класс PFoo везде,

где должен использоваться Foo* - например, при вызове функции g().

class PFoo {

private:

Foo* f;

public:

PFoo() : f(new Foo) {}

~PFoo() { delete f; }

operator Foo*() { return f; }

}

void g(Foo*);

{

PFoo p;

g(p); // Вызывает функцию operator Foo*() для преобразования

// Уничтожается p, а за ним - Foo

}

Обратите внимание, что этот класс не совсем безопасен, поскольку адрес, возвращаемый функцией operator Foo*(), становится недействительным после удаления вмещающего PFoo. Мы разберемся с этим чуть позже.

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

У стековых объектов есть еще одно преимущество - если ваш компилятор поддерживает ANSI-совместимую обработку исключений (exception). Когда во время раскрутки стека происходит исключение, деструкторы стековых объектов вызываются автоматически. Для динамических объектов этого не случается, и ваша куча может превратиться в настоящий хаос.




1. правовые нормы и институты
2. тематическая постановка задачи На складах фирмы А расположенных в трех пунктах отправления порты А1 А2 А3
3. ТЕМА- Деятельность комитетов по делам молодежи по реализации досуговых интересов подростков и молодежи
4. Особенности проявления психических состояний у сотрудников Методы их регуляции
5. Монтаж вертикальных цилиндрических резервуаров
6. Представления о добре и зле в истории цивилизации
7. Улитки ’ дары и бедствия приносящие.html
8. Роль людського капіталу у суспільному відтворенні
9. Иностранные инвестиции
10. а Древняя Русь Удельная Русь
11. Медицинская служба полка
12. Кюри и П.Кюри 1898
13. Реферат- Записная книжка на Delphi 7
14. ЧиМ Естествознание в нач школе призвано познакомить детей с конкретными явлениями окр мира объяснить
15. Апелляционное производство в системе пересмотра судебных актов Арбитражных судов
16. Реферат- Мыслители Древней Греции и Рима о культуре
17. При перевозке пассажиров выдаётся индивидуальный или групповой перевозочный документ содержащий- а указа
18. ВСЕУКРАИНСКИЙ АКЦИОНЕРНЫЙ БАНК новая редакция У Т В Е Р Д Ж Е Н О Решением Общего собрания акционеро
19. Грибоедов АС
20. Менеджмент Степанов В