Частные и общие данные

 

В уроке 21 вы создали свои первые классы в C++. При этом вы включали метку public в определение класса, чтобы обеспечить программе доступ к каждому элементу класса. Из данного урока вы узнаете, как атрибуты public и private управляют доступом к элементам класса со стороны программы. Вы узнаете, что ваши программы могут обратиться к общим (public) элементам из любой функции. С другой стороны, ваша программа может обращаться к частным (private) элементам только в функциях данного класса. Этот урок подробно исследует частные и общие элементы. К концу данного урока вы освоите следующие основные концепции:

Чтобы управлять тем, как ваши программы обращаются к элементам класса, C++ позволяет вам определять элементы как частные или общие.

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

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

 

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

 

СОКРЫТИЕ ИНФОРМАЦИИ

 

Как вы уже знаете, класс содержит данные и методы (функции). Для использования класса программы просто должны знать информацию, которую хранит класс (его элементы данных) и методы, которые манипулируют данными (функции). Вашим программам не требуется знать, как работают методы. Более того, программы должны знать только, какую задачу выполняют методы. Например, предположим, что у вас есть класс file. В идеале ваши программы должны знать только то, что этот класс обеспечивает методы file.print, который печатает отформатированную копию текущего файла, или file.delete, который удаляет файл. Вашей программе не требуется знать, как эти два метода работают. Другими словами, программа должна рассматривать класс как "черный ящик". Программа знает, какие методы необходимо вызвать и какие параметры им передать, но программа ничего не знает о рельной работе, выполняющейся внутри класса (в "черном ящике").

 

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

 

class employee

 

{

public:

char name [64];

long employee_id;

float salary;

void show_employee(void);

}

 

При создании класса вы могли бы иметь элементы, чьи значения используются только внутри класса, но обращаться к которым самой программе нет необходимости. Такие элементы являются частными (private), и их следует скрывать от программы. Если вы не используете метку public, то по умолчанию C++ подразумевает, что все элементы класса являются частными. Ваши программы не могут обращаться к частным элементам класса, используя оператор точку. К частным элементам класса могут обращаться только элементы самого класса. При создании класса вам следует разделить элементы на частные и общие, как показано ниже:

 

class some_class

 

{

public:

int some_variable;

void initialize_private(int, float); //———> Общие элементы

void show_data(void);

private:

int key_value; //—————————————> Частные элементы

float key_number;

}

 

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

 

some_class object; // Создать объект

object.some_variable = 1001;

object.initialize_private(2002, 1.2345);

object.show_data()

 

Если ваша программа пытается обратиться к частным элементам key_value или key_number, используя точку, компилятор сообщает о синтаксических ошибках.

 

Как правило, вы будете защищать элементы класса от прямого доступа к ним делая их частными. При этом программы не могут непосредственно присваивать значения таким элементам, используя оператор точку. Вместо того чтобы присвоить значение, программа должна вызвать метод класса. Предотвращая прямой доступ к элементам данных, вы, таким образом, можете гарантировать, что им всегда будут присваиваться допустимые значения. Например, предположим что объект nuclear_reactor вашей программы использует переменную с именем melt_down, которая всегда должна содержать значение в диапазоне от 1 до 5. Если элемент melt_down является общим, программа может непосредственно обратиться к элементу, изменяя его значение произвольным образом:

 

nuclear_reactor.melt_down = 101

 

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

 

int nuke::assign_meltdown(int value)

 

{

if ((value > 0) && (value <= 5))

 

{

melt_down = value;

return(0); // Успешное присваивание

} else return(-1); // Недопустимое значение

}

 

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

 

Общие и частные элементы

 

Классы C++ содержат данные и элементы. Чтобы указать, к каким элементам классов ваши программы могут обращаться напрямую, используя оператор точку, C++ позволяет вам определять элементы класса как общие {public) и частные (private). Таким образом, ваши программы могут непосредственно обращаться к любым общим элементам класса, используя оператор точку. С другой стороны, к частным элементам можно обратиться только через методы класса. Как правило, вы должны защищать большинство элементов данных класса, объявляя их частными. Следовательно, единственным способом, с помошью которого ваши программы могут присвоить значение элементам данных, является использование функций класса, которые способны проверить и скорректировать присваиваемые значения.

 

ИСПОЛЬЗОВАНИЕ ОБЩИХ И ЧАСТНЫХ ЭЛЕМЕНТОВ КЛАССА

 

Следующая программа INFOHIDE.CPP иллюстрирует использование общих и частных элементов класса. Программа определяет объект типа employee как показано ниже:

 

class employee

 

{

public:

int assign_values(char *, long, float);

void show_employee(void);

int change_salary(float);

long get_id(void);

private:

char name [64];

long employee_id;

float salary;

}

 

Как видите, класс защищает все свои элементы данных, объявляя их частными. Для доступа к элементам данных программа должна использовать интерфейсные функции. Ниже приведена реализация программы INFOHIDE.CPP:

 

#include <iostream.h>

 

#include <string.h>

 

class employee

 

{

public:

int assign_values(char *, long, float);

void show_employee(void);

int change_salary(float);

long get_id(void);

private:

char name [64];

long employee_id;

float salary;

);

 

int employee::assign_values(char *emp_name, long emp_id, float emp_salary)

 

{

strcpy(name, emp_name);

employee_id = emp_id;

if (emp_salary < 50000.0)

 

{

salary = emp_salary;

return(0); // Успешно

}

else

return(-1); // Недопустимый оклад }

 

void employee::show_employee(void)

 

{

cout << "Служащий: " << name << endl;

cout << "Номер служащего: " << employee_id << endl;

cout << "Оклад: " << salary << endl;

}

 

int employee::change_salary(float new_salary)

 

{

if (new_salary < 50000.0)

 

{

salary = new_salary;

return(0); // Успешно } else return(-1); // Недопустимый оклад }

 

long employee::get_id(void)

 

{

return(employee_id);

}

 

void main(void)

 

{

employee worker;

 

if (worker.assign_values("Happy Jamsa", 101, 10101.0) == 0)

 

{

cout << "Служащему назначены следующие значения" << endl;}

worker.show_employee();

if (worker.change_salary(35000.00) == 0)

 

{

    cout << "Назначен новый оклад" << endl;

    worker.show_employee();

}

}

else

cout << "Указан недопустимый оклад" << endl;

}

 

Выберите время, чтобы исследовать операторы программы более подробно. Несмотря на то что программа достаточно длинна, ее функции на самом деле очень просты. Метод assign_values инициализирует частные данные класса. Метод использует оператор if, чтобы убедиться, что присваивается допустимый оклад. Метод show_employee в данном случае выводит частные элементы данных. Методы change_salary и get_id представляют собой интерфейсные функции, обеспечивающие программе доступ к частным данным. После успешной компиляции и запуска этой программы отредактируйте ее и попытайтесь обратиться напрямую к частным элементам данных, используя оператор точку внутри main. Так как вы не можете напрямую обратиться к частным элементам, компилятор сообщит о синтаксических ошибках.

 

Что такое интерфейсные функции

 

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

 

ИСПОЛЬЗОВАНИЕ ОПЕРАТОРА ГЛОБАЛЬНОГО РАЗРЕШЕНИЯ ДЛЯ ЭЛЕМЕНТОВ КЛАССА

 

Если вы рассмотрите функции в программе INFOHIDE.CPP, вы обнаружите, что имена параметров функции часто предваряются символами етр_, как показано ниже:

 

int employee::assign_values(char *emp_name, long emp_id, float emp_salary)

 

Символы етр_ использовались, чтобы избежать конфликта между именами параметров и именами элементов класса. Если подобный конфликт имен всe же происходит, вы можете разрешить его, предваряя имена элементов класса именем класса и оператором глобального разрешения (::). Следующая функция использует оператор глобального разрешения и имя класса перед именем элементов класса. Исходя из этого, любой читающий эти операторы поймет, какие имена соответствуют классу employee:

 

 

int employee::assign_values(char *name, long employee_id, float salary)

 

{

strcpy(employee::name, name);

employee::employee_id = employee_id;

if (salary < 50000.0)

 

{

employee::salary = salary;

 

return(0); // Успешно } else

return(-1); // Недопустимый оклад

}

 

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

 

Использование оператора глобального разрешения для указания элементов класса

 

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

 

 

class_naine::member_name = some_value;

ЧАСТНЫЕ ЭЛЕМЕНТЫ КЛАССА НЕ ВСЕГДА ЯВЛЯЮТСЯ ДАННЫМИ

 

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

ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

 

Управляя доступом программы к элементам класса, вы снижаете возможность ошибок, которые происходят в результате злоупотребления этими элементами. Чтобы управлять доступом к элементам класса, можно использовать частные элементы. Большинство определений классов C++, которые вы встретите, будут использовать сочетание частных и общих элементов. Одна из наиболее широко используемых операций, которую ваши программы выполняют при создании объекта, представляет собой инициализацию элементов данных объекта. Из урока 23 вы узнаете, что C++ позволяет вам определять специальную функцию, называемую конструктором, которая автоматически вызывается каждый раз при создании объекта. Используя конструктор, ваша программа легко может инициализировать элементы класса. До изучения урока 23 убедитесь, что освоили следующие основные концепции:

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

Если это не оговорено явно, C++ считает все элементы частными.

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

При создании программы, манипулирующей элементами класса, вы можете предварять имя каждого элемента именем класса и оператором глобального разрешения (::), например employее::name, для избежания возможных конфликтов имен.

 

Урок 23. Конструктор и деструктор

 

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

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

Конструктор имеет такое же имя, как и класс.

Конструктор не имеет возвращаемого значения.

Каждый раз, когда ваша программа создает переменную класса, C++ вызывает конструктор класса, если конструктор существует.

Многие объекты могут распределять память для хранения информации; когда вы уничтожаете такой объект, C++ будет вызывать специальный деструктор, который может освобождать эту память, очищая ее после объекта.

Деструктор имеет такое же имя, как и класс, за исключением того, что вы должны предварять его имя символом тильды (~).

Деструктор не имеет возвращаемого значения.

 

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

 

СОЗДАНИЕ ПРОСТОГО КОНСТРУКТОРА

 

Конструктор представляет собой метод класса, который имеет такое же имя, как и класс. Например, если вы используете класс с именем employee, конструктор также будет иметь имя employee. Подобно этому, для класса с именем dogs конструктор будет иметь имя dogs. Если ваша программа определяет конструктор, C++ будет автоматически вызывать его каждый раз, когда вы создаете объект. Следующая программа CONSTRUC.CPP создает класс с именем employee. Программа также определяет конструктор с именем employee который присваивает начальные значения объекту. Однако конструктор не возвращает никакого значения, несмотря на то, что он не объявляется как void. Вместо этого вы просто не указываете тип возвращаемого значения:

 

class employee

 

{

public:

employee(char *, long, float); //Конструктор

void show_employee(void);

int change_salary(float);

long get_id(void);

private:

char name [64];

long employee_id;

float salary;

};

 

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

 

employee::employee(char *name, long employee_id, float salary)

 

{

strcpy(employee::name, name);

employee::employee_id = employee_id;

if (salary < 50000.0)

employee::salary = salary;

else // Недопустимый оклад

employee::salary = 0.0;

}

 

Как видите, конструктор не возвращает значение вызвавшей функции. Для него также не используется тип void. В данном случае конструктор использует оператор глобального разрешения и имя класса перед именем каждого элемента, как уже обсуждалось. Ниже приведена реализация программы CONSTRUC.CPP:

 

#include <iostream.h>

 

#include <string.h>

 

class employee

 

{

public:

employee(char *, long, float);

void show_employee(void);

int change_salary(float);

long get_id(void);

private:

char name [64];

long employee_id;

float salary;

};

 

employee::employee(char *name, long employee_id, float salary)

 

{

strcpy(employee::name, name);

employee::employee_id = employee_id;

if (salary < 50000.0)

employee::salary = salary;

else // Недопустимый оклад

employee::salary = 0.0;

}

 

void employee::show_employee(void)

 

{

cout << "Служащий: " << name << endl;

cout << "Номер служащего: " << employee_id << endl;

cout << "Оклад: " << salary << endl;

}

 

void main(void)

 

{

employee worker("Happy Jamsa", 101, 10101.0);

worker.show_employee();

}

 

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

 

employee worker("Happy Jamsa", 101, 10101.0);

 

Если вашей программе потребуется создать несколько объектов employee, вы можете инициализировать элементы каждого из них с помощью конструктора, как показано ниже:

 

employee worker("Happy Jamsa", 101, 10101.0);

 

employee secretary("John Doe", 57, 20000.0);

 

employee manager("Jane Doe", 1022, 30000.0);

 

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

 

Конструктор представляет собой специальную функцию, которую C++ автоматически вызывает каждый раз при создании объекта. Обычное назначение конструктора заключается в инициализации элементов данных объекта. Конструктор имеет такое же имя, как и класс. Например, класс с именем file использует конструктор с именем file. Вы определяете конструктор внутри своей программы так же, как и любой метод класса. Единственное различие заключается в том, что конструктор не имеет возвращаемого значения. Когда вы позже объявляете объект, вы можете передавать параметры конструктору, как показано ниже:

 

class_name object(valuel, value2, value3)

Конструкторы и параметры по умолчанию

 

Как вы уже знаете из урока 15, C++ позволяет указывать значения по умолчанию для параметров функции. Если пользователь не указывает каких-либо параметров, функция будет использовать значения по умолчанию. Конструктор не является исключением; ваша программа может указать для него значения по умолчанию так же, как и для любой другой функции. Например, следующий конструктор employee использует по умолчанию значение оклада равным 10000.0, если программа не указывает оклад при создании объекта. Однако программа должна указать имя служащего и его номер:

 

employee::employee(char *name, long employee_id, float salary = 10000.00)

 

{

strcpy(employee::name, name);

employee::employee_id = employee_id;

if (salary < 50000.0)

employee::salary = salary;

else // Недопустимый оклад

employee::salary = 0.0;

}

Перегрузка конструкторов

 

Как вы уже знаете из урока 13, C++ позволяет вашим программам перегружать определения функций, указывая альтернативные функции для других типов параметров. C++ позволяет вам также перегружать конструкторы. Следующая программа CONSOVER.CPP перегружает конструктор employee. Первый конструктор требует, чтобы программа указывала имя служащего, номер служащего и оклад. Второй конструктор запрашивает пользователя ввести требуемый оклад, если программа не указывает его:

 

employee::employee(char *name, long employee_id)

 

{

strcpy(employee::name, name);

employee::employee_id = employee_id;

do

 

{

cout << "Введите оклад для " << name << " меньше $50000: ";

cin >> employee::salary;

}

while (salary >= 50000.0);

}

 

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

 

class employee

 

{

public:

employee (char *, long, float);|___ Прототипы перегруженных

employee(char *, long);     |функций

void show_employee(void);

int change_salary(float);

long get_id(void);

private:

char name [64];

long employee_id;

float salary;

}

 

Ниже приведена реализация программы CONSOVER.CPP:

 

#include <iostream.h>

 

#include <string.h>

 

class employee

 

{

public:

employee(char *, long, float);

employee(char *, long);

void show_employee(void);

int change_salary(float);

long get_id(void);

private:

char name [64];

long employee_id;

float salary;

};

 

employee::employee(char *name, long employee_id, float salary)

 

{

strcpy(employee::name, name);

 employee::employee_id = employee_id;

if (salary < 50000.0) employee::salary = salary;

else // Недопустимый оклад

employee::salary = 0.0;

}

 

employee::employee(char *name, long employee_id)

 

{

strcpy(employee::name, name);

employee::employee_id = employee_id;

do

 

{

cout << "Введите оклад для " << name << " меньше $50000: ";

cin >> employee::salary;

}

while (salary >= 50000.0);

}

 

void employee::show_employee(void)

 

{

cout << "Служащий: " << name << endl;

cout << "Номер служащего: " << employee_id << endl;

cout << "Оклад: " << salary << endl;

}

 

void main(void)

 

{

employee worker("Happy Jamsa", 101, 10101.0);

employee manager("Jane Doe", 102);

worker.show_employee();

manager.sbow_employee();

}

 

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

 

ПРЕДСТАВЛЕНИЕ О ДЕСТРУКТОРЕ

 

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

 

Каждая из созданных вами до сих пор программ создавала объекты в самом начале своего выполнения, просто объявляя их. При завершении программ C++ уничтожал объекты. Если вы определяете деструктор внутри своей программы, C++ будет автоматически вызывать деструктор для каждого объекта, когда программа завершается (т.е. когда объекты уничтожаются). Подобно конструктору, деструктор имеет такое же имя, как и класс объекта. Однако в случае деструктора вы предваряете его имя символом тильды (~), как показано ниже:

 

~class_name (void) //----------->указывает деструктор

 

{

// Операторы деструктора

}

 

В отличие от конструктора вы не можете передавать параметры деструктору. Следующая программа DESTRUCT.CPP определяет деструктор для класса employee:

 

void employee::-employee(void)

 

{

cout << "Уничтожение объекта для " << name << endl;

}

 

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

 

#include <iostream.h>

 

#include <string.h>

 

class employee

 

{

public:

employee(char *, long, float);

~employee(void);

void show_employee(void);

int change_salary(float);

long get_id(void);

private:

char name [64];

long employee_id;

float salary;

};

 

employee::employee(char *name, long employee_id, float salary)

 

{

strcpy(employee::name, name);

employee::employee_id = employee_id;

if (salary < 50000.0) employee::salary = salary;

else // Недопустимый оклад

employee::salary в 0.0;

}

 

void employee::-employee(void)

 

{

cout << "Уничтожение объекта для " << name << endl;

}

 

void employee::show_employee(void)

 

{

cout << "Служащий: " << name << endl;

cout << "Номер служащего: " << employee_id << endl;

cout << "Оклад: " << salary << endl;

}

 

void main(void)

 

{

employee worker("Happy Jamsa", 101, 10101.0);

worker.show_employee();

}

 

Если вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:

 

С:\> DESTRUCT <ENTER>

 

Служащий: Happy Jamsa

 

Номер служащего: 101

 

Оклад: 10101

 

Уничтожение объекта для Happy Jamsa

 

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

 

Деструкторы

 

Деструктор представляет собой функцию, которую C++ автоматически запускает, когда он или ваша программа уничтожает объект. Деструктор имеет такое же имя, как и класс объекта; однако вы предваряете имя деструктора символом тильды (~), например ~employee. В своей программе вы определяете деструктор точно так же, как и любой другой метод класса.

 

ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

 

Конструкторы и деструкторы представляют собой специальные функции класса, которые ваша программа автоматически вызывает при создании или уничтожении объекта. Большинство программ используют конструктор для инициализации элементов данных класса. Простые программы, создаваемые сейчас вами, вероятно, не потребуют использования деструктора. Из урока 24 вы узнаете, как перегружать операторы. Другими словами, вы можете переопределить символ плюс таким образом, что он будет добавлять содержимое одной строки к другой. Как вы уже знаете, тип (например, char, float и int) определяет набор значений, которые может хранить переменная, и набор операций, которые ваши программы могут выполнять над этой переменной. Когда вы определяете класс, вы по существу определяете тип. C++ позволяет вам указать, как ведут себя операторы с данным типом. До изучения урока 24 убедитесь, что освоили следующие основные концепции:

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

Конструктор не имеет возвращаемого значения, но вы не указываете ему тип void. Вместо этого вы просто не указываете возвращаемое значение вообще.

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

C++ позволяет вам перегружать конструкторы и разрешает использовать значения по умолчанию для параметров.

Деструктор представляет собой специальную функцию, которую ваша программа вызывает автоматически каждый раз при уничтожении объекта. Деструктор имеет такое же имя, как и класс объекта, но его имя предваряется символом тильды (~).

 

Урок 24. Перегрузка операторов

 

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

 

Перегрузка оператора состоит в изменении смысла оператора (например, оператора плюс (+), который обычно в C++ используется для сложения) при использовании его с определенным классом. В данном уроке вы определите класс string и перегрузите операторы плюс и минус. Для объектов типа string оператор плюс будет добавлять указанные символы к текущему содержимому строки. Подобным образом оператор минус будет удалять каждое вхождение указанного символа из строки. К концу данного урока вы изучите следующие основные концепции:

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

Для перегрузки операторов программы используют ключевое слово C++ operator.

Переопределяя оператор, вы указываете функцию, которую C++ вызывает каждый раз, когда класс использует перегруженный оператор. Эта функция, в свою очередь, выполняет соответствующую операцию.

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

C++ позволяет перегружать большинство операторов, за исключением четырех, перечисленных в таблице 24, которые программы не могут перегружать.

 

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

 

ПЕРЕГРУЗКА ОПЕРАТОРОВ ПЛЮС И МИНУС

 

Когда вы перегружаете оператор для какого-либо класса, то смысл данного оператора не изменяется для переменных других типов. Например, если вы перегружаете оператор плюс для класса string, то смысл этого оператора не изменяется, если необходимо сложить два числа. Когда компилятор С++ встречает в программе оператор, то на основании типа переменной он определяет ту операцию, которая должна быть выполнена.

 

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

 

class string

 

{

public:

string(char *); // Конструктор

void str_append(char *);

void chr_minus(char);

void show_string(void);

private:

char data[256];

};

 

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

 

#include <iostream.h>

 

#include <string.h>

 

class string

 

{

public:

string(char *); // Конструктор

void str_append(char *);

void chr_minus(char);

void show_string(void);

private:

char data[256];

};

 

string::string(char *str)

 

{

strcpy(data, str);

}

 

void string::str_append(char *str)

 

{

strcat(data, str);

}

 

void string::chr_minus(char letter)

 

{

char temp[256];

int i, j;

for (i = 0, j = 0; data[i]; i++) // Эту букву необходимо удалить?

if (data[i]!= letter) // Если нет, присвоить ее temp

temp[j++] = data[i];

temp[j] = NULL; // Конец temp

// Копировать содержимое temp обратно в data

strcpy(data, temp);

}

 

void string::show_string(void)

 

{

cout << data << endl;

}

 

void main(void)

 

{

string title("Учимся программировать на языке C++");

string lesson("Перегрузка операторов");

title.show_string();

title.str_append(" я учусь!");

itle.show_string();

lesson.show_string();

lesson.chr_minus('p');

lesson.show_string();

}

 

Как видите, программа использует функцию str_append для добавления символов к строковой переменной title. Программа также использует функцию chr_minus для удаления каждой буквы " р" из символьной строки lesson. В данном случае программа использует вызовы функции для выполнения этих операций. Однако, используя перегрузку операторов, программа может выполнять идентичные операции с помощью операторов плюс (+) и минус (-).

 

При перегрузке оператора используйте ключевое слово C++ operator вместе с прототипом и определением функции, чтобы сообщить компилятору C++, что класс будет использовать этот метод как оператор. Например, следующее определение класса использует ключевое слово operator, чтобы назначить операторы плюс и минус функциям str_append и chr_minus внутри класса string:

 

class string

 

{

public:

string(char *); // Конструктор

void operator +(char *);

void operator -(char); ————— Определение операторов класса void show_string(void);

private:

char data[256];

};

 

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

 

void string::operator +(char *str)

 

{

strcat(data, str);

}

 

Как видите, определение этой функции не содержит имени, поскольку здесь определяется перегруженный оператор класса. Для перегрузки оператора плюс программа не изменила обработку, которая осуществляется внутри функции (код этой функции идентичен коду предыдущей функции str_append). Вместо этого программа просто заменила имя функции ключевым словом operators соответствующим оператором. Следующая программа OPOVERLD.CPP иллюстрирует использование перегружаемых операторов плюс и минус:

 

#include <iostream.h>

 

#include <string.h>

 

class string

 

{

public:

string(char *); // Конструктор

void operator +(char *);

void operator -(char);

void show_string(void);

private;

char data[256];

};

 

string::string(char *str)

 

{

strcpy(data, str);

}

 

void string::operator +(char *str)

 

{

strcat(data, str);

}

 

void string::operator -(char letter)

 

{

 char temp[256];

int i, j;

for (i = 0, j = 0; data[i]; i++) if (data[il 1= letter) temp[j++] = data[i];

temp[j] = NULL;

strcpy(data, temp);

}

 

void string::show_string(void)

 

{

cout << data << endl;

}

 

void main(void)

 

{

string title("Учимся программировать на C++");

string lesson("Перегрузка операторов");

title.show_string();

title + " я учусь!";

title.show_string();

lesson.show_string();

lesson - 'P';

lesson.show_string();

}

 

Как видите, программа использует перегруженные операторы:

 

title + " я учусь!"; // Добавить текст " я учусь!"

 

lesson - 'р'; // Удалить букву 'р'

 

В данном случае синтаксис оператора законен, но немного непривычен. Обычно вы используете оператор плюс в выражении, которое возвращает результат, например, как в операторе some_str = title + "текст ";. Когда вы определяете оператор, C++ предоставляет вам полную свободу в отношении поведения оператора. Однако, как вы помните, ваша цель при перегрузке операторов состоит в том, чтобы упростить понимание ваших программ. Поэтому следующая программа STR_OVER.CPP немного изменяет предыдущую программу, чтобы позволить ей выполнять операции над переменными типа string, используя синтаксис, который более согласуется со стандартными операторами присваивания:

 

#include <iostream.h>

 

#include <string.h>

 

class string

 

{

public:

string(char *); // Конструктор

char * operator +(char *);

char * operator -(char);

void show_string(void);

private:

char data[256];

};

 

string::string(char *str)

 

{

strcpy(data, str);

}

 

char * string::operator +(char *str)

 

{

return(strcat(data, str));

}

 

char * string::operator -(char letter)

 

{

char temp[256];

int i, j;

for (i = 0, j = 0; data[i]; i++) if (data[i] 1= letter) temp[j++] = data[i];

temp[j] = NULL;

return(strcpy(data, temp));

}

 

void string::show_string(void)

 

{

cout << data << endl;

}

 

void main(void)

 

{

string title("Учимся программировать на C++");

string lesson("Перегрузка операторов");

title.show_string();

title = title + " я учусь";

title.show_string();

lesson.show_string();

lesson = lesson - '?';

lesson.show_string();

}

 

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

 

title = title + " учимся программировать!";

 

lesson = lesson - 'р';

Второй пример

 

При создании ваших собственных типов данных с помощью классов наиболее общей операцией будет проверка, являются ли два объекта одинаковыми. Используя перегрузку, ваши программы могут перегрузить операторы равенства (==), неравенства (!=) или другие операторы сравнения. Следующая программа COMP_STR.CPP добавляет новый оператор в класс string, который проверяет, равны ли два объекта string. Используя перегрузку операторов, ваши программы могут проверять, содержат ли строковые объекты одинаковые строки, как показано ниже:

 

if (some_string == another_string)

 

Ниже приведена реализация программы COMP_STR.CPP:

 

#include <iostream.h>

 

#include <string.h>

 

class string

 

{

public:

string(char *); // конструктор

char * operator +(char *);

char * operator -(char);

int operator ==(string);

void show_string(void);

private:

char data[256];

};

 

string::string(char *str)

 

{

strcpy(data, str);

}

 

char * string::operator +(char *str)

 

{

return(strcat(data, str));

}

 

char * string::operator -(char letter)

 

{

char temp[256];

int i, j;

for (i = 0, j = 0; data[i]; i++) if (data[i] 1= letter) temp[j++] = data[i];

temp[j] = NULL;

return(strcpy(data, temp));

}

 

int string::operator ==(string str)

 

{

int i;

for (i = 0; data[i] == str.data[i]; i++)

if ((data[i] == NULL) && (str.data[i] == NULL)) return(1); // Равно

return (0); //He равно

}

 

void string::show_string(void)

 

{

cout << data << endl;

}

 

void main(void)

 

{

string title("Учимся программировать на C++");

string lesson("Перегрузка операторов");

string str("Учимся программировать на C++");

if (title == lesson) cout << "title и lesson равны" << endl;

if (str == lesson) cout << "str и lesson равны" << endl;

if (title == str) cout << "title и str равны" << endl;

}

 

Как видите, перегружая операторы подобным образом, вы упрощаете понимание ваших программ.

ОПЕРАТОРЫ, КОТОРЫЕ Вbl HE МОЖЕТЕ ПЕРЕГРУЗИТЬ

 

В общем случае ваши программы могут перегрузить почти все операторы С++. В табл. 24 перечислены операторы, которые C++ не позволяет перегружать.

 

Таблица 24. Операторы C++, которые ваши программы не могут перегрузить.

Оператор

 

     

Назначение  

Пример

. Выбор элемента object.member

.* Указатель на элемент object.*member

:: Разрешение области видимости   classname::member

 

?:    

Условный оператор сравнения с = (а > b)? а: b;

 

ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

 

Перегрузка операторов — это возможность назначать новый смысл операторам при использовании их с определенным классом. Используя перегрузку операторов, вы можете повысить удобочитаемость ваших программ и облегчить их понимание, выражая операции класса более понятным образом. Из урока 25 вы узнаете, как разделить данные между объектами с помощью элемента static и как использовать методы класса, когда никакие объекты класса не объявляются. До изучения урока 25 убедитесь, что вы освоили следующее:

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

Когда вы перегружаете оператор, перегрузка действует только для класса, в котором он определяется. Если программа использует оператор с неклассовыми переменными (например, переменными типа int или float), используется стандартное определение оператора.

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

C++ не позволяет вашим программам перегружать оператор выбора элемента (.), оператор указателя на элемент (.*), оператор разрешения области видимости (::) и условный оператор сравнения (?:).

 

Урок 25. Статические функции и элементы данных

 

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

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

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

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

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

Ваши программы могут использовать ключевое слово 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 << '\033' << "[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);

 

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

 

menu::clear_screen();

ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

 

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

Когда вы объявляете элемент класса как static, то такой элемент может совместно использоваться всеми объектами данного класса.

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

Если вы объявляете элемент как public и static, ваша программа может использовать такой элемент, даже если объекты данного класса не существуют. Для обращения к этому элементу ваша программа должна использовать оператор глобального разрешения, например class_name::member_name.

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

 

Урок 26. Наследование

 

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

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

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

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

В дополнение к общим (public) (доступным всем) и частным (private) (доступным методам класса) элементам C++ предоставляет защищенные (protected) элементы, которые доступны базовому и производному классам.

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

 

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

ПРОСТОЕ НАСЛЕДОВАНИЕ

 

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

 

class employee

 

{

public:

employee(char *, char *, float);

void show_employee(void);

private:

char name[64];

char position[64];

float salary;

};

 

Далее предположим, что вашей программе требуется класс manager, который добавляет следующие элементы данных в класс employee:

 

float annual_bonus;

char company_car[64];

int stock_options;

 

В данном случае ваша программа может выбрать два варианта: во-первых, программа может создать новый класс manager, который дублирует многие элементы класса employee, или программа может породить класс типа manager из базового кла


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: