C++ позволяет друзьям определенного класса обращаться к частным элементам этого класса. Чтобы указать C++, что один класс является другом (friend) другого класса, вы просто помещаете ключевое слово friend и имя соответствующего класса-друга внутрь определения этого другого класса. Например, приведенный ниже класс book объявляет класс librarian своим другом. Поэтому объекты класса librarian могут напрямую обращаться к частным элементам класса book, используя оператор точку:
Как видите, чтобы указать друга, необходим только один оператор внутри определения класса. Например, следующая программа VIEWBOOK.CPP использует librarian в качестве друга класса book. Следовательно, функции класса librarian могут напрямую обращаться к частным элементам класса book. В данном случае программа использует функцию change_catalog класса librarian для изменения номера карточки каталога определенной книги:
Обычной файловой операцией в ваших программах является чтение содержимого файла, пока не встретится конец файла. Чтобы определить конец файла, ваши программы могут использовать функцию еоf потокового объекта. Эта функция возвращает значение 0, если конец файла еще не встретился, и 1, если встретился конец файла. Используя цикл while, ваши программы могут непрерывно читать содержимое файла, пока не найдут конец файла, как показано ниже:
В данном случае программа будет продолжать выполнять цикл, пока функция eof возвращает ложь (0). Следующая программа TEST_EOF.CPP использует функцию eof для чтения содержимого файла BOOKINFO.DAT, пока не достигнет конца файла:
Аналогично, следующая программа WORD_EOF.CPP читает содержимое файла по одному слову за один раз, пока не встретится конец файла:
И наконец, следующая программа CHAR_EOF.CPP читает содержимое файла по одному символу за один раз, используя функцию get, пока не встретит конец файла:
В предыдущем классе employee функция была определена внутри самого класса (встроенная (inline) функция). При увеличении функций определение встроенных функций внутри класса может внести беспорядок в описание класса. В качестве альтернативы вы можете поместить прототип функции внутри класса, а затем определить функцию вне класса. Ваше определение класса с прототипом становится следующим:
Так как разные классы могут использовать функции с одинаковыми именами, вы должны предварять имена определяемых вне класса функций именем класса и оператором глобального разрешения (::). В данном случае определение функции становится следующим:
Как видите, приведенный код предваряется определением функции с именем класса (employee) и оператором глобального разрешения (::). Следующая программа CLASSFUN.CPP помещает определение функции show_employee вне класса, используя оператор глобального разрешения для указания имени класса:
Когда ваша программа генерирует исключительную ситуацию, C++ запускает обработчик исключительной ситуации (функцию), чьи операторы вы определили в классе исключительной ситуации. Например, следующий класс исключительной ситуации nuke_meltdown определяет операторы обработчика исключительной ситуации в функции nuke_meltdown:
В данном случае, когда программа сгенерирует исключительную ситуацию nuke_meltdown, C++ запустит операторы функции nuke_meltdown, прежде чем возвратит управление первому оператору, следующему за оператором try, разрешающему обнаружение исключительной ситуации. Следующая программа MELTDOWN.CPP иллюстрирует использование функции nuke_meltdown. Эта программа использует оператор try для разрешения обнаружения исключительной ситуации. Далее программа вызывает функцию add_u232 c параметром amount. Если значение этого параметра меньше 255, функция выполняется успешно. Если же значение параметра превышает 255, функция генерирует исключительную ситуацию nuke_meltdown:
Если вы откомпилируете и запустите эту программу, на экране дисплея появится следующий вывод:
Если вы проследите исходный код, который генерирует каждое из сообщений, то сможете убедиться, что поток управления при возникновении исключительной ситуации проходит в обработчик исключительной ситуации и обратно к оператору catch. Так, первая строка вывода генерируется обработчиком исключительной ситуации, т.е. функцией nuke_meltdown. Вторая строка вывода генерируется в операторе catch, который обнаружил исключительную ситуацию.
Обеспечить значения по умолчанию для параметров функции очень легко. Вы просто присваиваете значение параметру с помощью оператора присваивания С++ прямо при объявлении функции, как показано ниже:
Следующая программа DEFAULTS. CPP присваивает значения по умолчанию параметрам a, b и c внутри функции show_parameters. Затем программа четыре раза вызывает эту функцию, сначала не указывая параметров вообще, затем указывая значение только для а, потом значения для а и b и, наконец, указывая значения для всех трех параметров:
Когда вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:
Как видите, если необходимо, функция использует значения параметров по умолчанию.
Независимо от назначения большинство ваших программ на C++ будут складывать, вычитать, умножать или делить. Вы узнаете, что ваши программы могут выполнять арифметические операции с константами (например, 3*5) или с переменными (например, payment — total). Таблица 5.1 перечисляет основные математические операции C++:
Таблица 5. Основные математические операции С++
|
Операция
|
Назначение
|
Пример
|
+
|
Сложение
|
total = cost + tax;
|
-
|
Вычитание
|
change = payment - total;
|
*.
|
Умножение
|
tax = cost * tax_rate;
|
/
|
Деление
|
average = total / count;
Следующая программа SHOWMATH.CPP использует cout для вывода реультата нескольких простых арифметических операций:
#include iostream.h
void main(void)
{
cout "5 + 7 = " 5 + 7 endl;
cout "12 - 7 = " 12 - 7 endl;
cout "1.2345 * 2 = " 1.23.45 * 2 endl;
cout "15 / 3 = " 15 / 3 endl;
}
Посмотрите внимательно на операторы программы. Обратите внимание, что каждое выражение сначала появляется в кавычках, которые обеспечивают вывод символов (например, 5 + 7 =) на экран. Затем программа выводит результат операции и символ новой строки. Когда вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:
С:\ SHOWMATH ENTER
+ 7 = 12
- 7 = 5
* 2 = 2.469
/ 3 = 5
В данном случае программа выполняла арифметические операции, используя только постоянные значения. Следующая программа MATHVARS.CPP выполняет арифметические операции, используя переменные:
#include iostream.h
void main(void)
{
float cost =15.50; // Стоимость покупки
float sales_tax = 0.06; // Налог на продажу 6%
float amount_paid = 20.00; // Деньги покупателя
float tax, change, total; // Налог на продажу, сдача покупателю и общий счет
tax = cost * sales_tax;
total = cost + tax;
change = amount_paid - total;
cout "Стоимость покупки: $" cost "\tHaлor: $" tax "\tОбщий счет: $" total endl;
cout "Сдача покупателю: $" change endl;
}
В данном случае программа использует только переменные с плавающей точкой. Как видите, программа присваивает значения переменным при объявлении. Далее программа выполняет арифметические операции над переменными для определения налога на продажу, общего счета и сдачи покупателю. Когда вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:
С:\ MATHVARS ENTER
Стоимость покупки: $15.5 Налог: $0.93 Общий счет: $16.43
Сдача покупателю: $3.57
ОСВОБОЖДЕНИЕ ПАМЯТИ, ЕСЛИ ОНА БОЛЬШЕ НЕ НУЖНА
Как вы знаете, оператор C++ new позволяет вашим программам выделять память динамически во время выполнения. Если вашей программе больше не нужна выделенная память, она должна ее освободить, используя оператор delete. Для освобождения памяти с использованием оператора delete вы просто указываете этому оператору указатель на данную область памяти, как показано ниже:
delete pointer;
Следующая программа DEL_MEM.CPP использует оператор delete для освобождения выделенной с помощью оператора new памяти:
#include iostream.h
#include string.h
void main(void)
{
char *pointer = new char[100];
strcpy(pointer, "Учимся программировать на языке C++");
cout pointer endl;
delete pointer;
}
По умолчанию, если ваша программа не освобождает выделенную ей память до своего завершения, операционная система автоматически освобождает эту память после завершения программы. Однако если ваша программа использует оператор delete для освобождения памяти по мере того, как она (память) становится ненужной, то эта память вновь становится доступной для других целей (возможно, для вашей программы, которая опять будет использовать оператор new, или для операционной системы).
ПЕРЕДАЧА МАССИВОВ В ФУНКЦИИ
Ващи программы будут передавать массивы в функции точно так же, как и любые другие переменные. Функция может инициализировать массив, прибавить к массиву значения или вывести элементы массива на экран. Когда вы передаете массив в функцию, вы должны указать тип массива. Нет необходимости указывать размер массива. Вместо этого вы передаете параметр например number_of_elements, который содержит количество элементов в массиве:
void some_function(int array[], int number_of_elements);
Следующая программа ARRAYFUN.CPP передает массивы в функцию show_array, которая использует цикл for для вывода значений массивов:
#include iostream.h
void show_array (int array [] , int number_of_elements)
{
int i;
for (i = 0; i number_of_elements; i++) cout array[i] ' ';
cout endl;
}
void main(void)
{
int little_numbers[5] ={1,2,3,4,5};
int big_numbers[3] = { 1000, 2000, 3000 };
show_array(little_numbers, 5);
show_array(big_numbers, 3);
}
Как видите, программа просто передает массив в функцию по имени, а также указывает параметр, который сообщает функции количество элементов, содержащихся в массиве:
show_array(little_numbers, 5);
Следующая программа GETARRAY.CPP использует функцию get_values, чтобы присвоить три значения массиву numbers:
#include iostream.h
void get_values(int array[], int number_of_elements)
{
int i;
for (i = 0; i number_of_elements; i++)
{
cout “ "Введите значение " i ": ";
cin ” array [i];
}
}
void main(void)
{
int numbers[3];
get_values(numbers, 3);
cout "Значения массива" endl;
for (int i = 0; i 3; i++)
cout numbers [i] endl;
}
Как видите, программа передает массив в функцию по имени. Функция в свою очередь присваивает массиву элементы. Из урока 10 вы узнали, что, пока ваша программа не передаст параметры в функцию с помощью адреса, функция не может изменить эти параметры. Однако, как можно заметить в данном случае, функция get_values изменяет параметр-массив numbers. Как вы узнаете из урока 20, C++ действительно передает массивы в функцию, используя указатели. Таким образом, функция может изменить элементы массива, если ей это нужно.
ПЕРЕДАЧА СТРОК В ФУНКЦИИ
Передача символьной строки в функцию подобна передаче любого массива в качестве параметра. Внутри функции вам нужно просто указать тип массива (char) и левую и правую скобки массива. Вам не надо указывать размер строки. Например, следующая программа SHOW_STR.CPP использует функцию show_string для вывода символьной строки на экран:
#include iostream.h
void show_string(char string[])
{
cout string endl;
}
void main(void)
{
show_string("Привет, C++!");
show_string("Учусь программировать на C++");
}
Как видите, функция show_string трактует параметр символьной строки как массив:
void show_string(char string[])
Так как символ NULL указывает конец строки, функция не требует параметр, который задает количество элементов в массиве. Вместо этого функция может определить последний элемент, просто найдя в массиве символ NULL.
Как вы уже знаете, функции C++ часто используют символ NULL для определения конца строки. Следующая программа STR_LEN.CPP создает функцию с именем string_length, которая ищет символ NULL в строке для определения количества символов, содержащихся в строке. Далее функция использует оператор return для возврата длины строки вызвавшей функции. Программа передает несколько различных символьных строк в функцию, отображая длину каждой из них на экране:
#include iostream.h
int string_length(char string[])
{
int i;
for (i = 0; string[] != '\0'; i++); // Ничего не делать, но перейти к
// следующему символу return(i); Длина строки
}
void main(void)
{
char title[] = "Учимся программировать на языке C++";
char lesson[] = "Символьные строки";
cout "Строка " title " содержит " string_length(title) " символов" endl;
cout "Строка " lesson " содержит " string_length(lesson) " символов" endl;
}
Как видите, функция запускается с первого символа строки (элемент 0) и затем исследует каждый элемент до тех пор, пока не встретит NULL. Рассматривая программы на C++, вы встретите целый ряд функций, которые подобным образом просматривают символьные строки в поисках символа NULL.
Перегрузка функций
При определении функций в своих программах вы должны указать тип возвращаемого функцией значения, а также количество параметров и тип каждого из них. В прошлом (если вы программировали на языке С), когда у вас была функция с именем add_values, которая работала с двумя целыми значениями, а вы хотели бы использовать подобную функцию для сложения трех целых значений, вам следовало создать функцию с другим именем. Например, вы могли бы использовать add_two_values и add_three_values. Аналогично если вы хотели использовать подобную функцию для сложения значений типа float, то вам была бы необходима еще одна функция с еще одним именем. Чтобы избежать дублирования функции, C++ позволяет вам определять несколько функций с одним и тем же именем. В процессе компиляции C++ принимает во внимание количество аргументов, используемых каждой функцией, и затем вызывает именно требуемую функцию. Предоставление компилятору выбора среди нескольких функций называется перегрузкой. В этом уроке вы научитесь использовать перегруженные функции. К концу данного урока вы освоите следующие основные концепции:
• Перегрузка функций позволяет вам использовать одно и то же имя для нескольких функций с разными типами параметров.
• Для перегрузки функций просто определите две функции с одним и тем же именем и типом возвращаемого значения, которые отличаются количеством параметров или их типом.
Перегрузка функций является особенностью языка C++, которой нет в языке С. Как вы увидите, перегрузка функций достаточно удобна и может улучшить удобочитаемость ваших программ.
Перегрузка конструкторов
Как вы уже знаете из урока 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. Когда вы введете оклад, программа отобразит информацию об обоих служащих.
Перегрузка операторов
Как вы уже знаете, тип переменной определяет набор значений, которые она может хранить, а также набор операций, которые можно выполнять над этой переменной. Например, над значением переменной типа 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 - 'р';
ПЕРВОЕ ЗНАКОМСТВО С cin
Точно так же как выходной поток cout позволяет вашим программам записать вывод на экран, входной поток cin позволяет программам читать ввод с клавиатуры. Когда программы используют cin для чтения ввода с клавиатуры, они должны указать переменную, в которую cin поместит данные. Следующая программа FIRSTCIN.CPP использует cin для чтения числа, введенного с клавиатуры. Программа присваивает введенное число переменной с именем number, а затем выводит значение переменной, используя выходной поток cout:
#include iostream.h
void main(void)
{
int number; II Число, читаемое с клавиатуры
cout "Введите ваше любимое число и нажмите Enter: ";
cin number;
cout "Ваше любимое число равно " number endl;
}
Когда вы откомпилируете и запустите эту программу, на вашем экране появится сообщение, предлагающее вам ввести ваше любимое число. Если вы введете число и нажмете ENTER, программа присвоит ввод переменной number. Затем, используя cout, программа выведет сообщение, отображающее ваше любимое число.
Следующая программа TWONBRS.CPP запрашивает у вас два числа. Программа присваивает числа переменным first и second. Затем программа выводит числа, используя cout:
#include iostream.h
void main(void)
{
int first, second; // Числа, введенные с клавиатуры
cout "Введите два числа и нажмите Enter: ";
cin first second;
cout "Были введены числа " first " и " second endl;
}
Обратите внимание на использование с cin двух операторов извлечения:
cin first second;
В этом случае cin присвоит первое введенное значение переменной first, a второе переменной second. Если для вашей программы требуется третье значение, вы можете использовать третий оператор извлечения, как показано ниже:
cin first second third;
Если вы применяете cin для чтения чисел с клавиатуры, cin использует левый пустой символ (пробел, табуляцию, возврат каретки), чтобы определить, где начинается одно значение, а где второе. Экспериментируйте с программой TWONBRS, разделяя числа табуляцией, пробелом и возвратом каретки.
Чтение ввода с клавиатуры с помощью cin
Для чтения ввода с клавиатуры программы могут использовать входной поток cin. При использовании cin вы должны указать переменную, в которую cin помещает данные. Затем используйте оператор извлечения () для направления данных, как показано ниже:
cin some_variable;
Оператор извлечения называется так, потому что он извлекает (удаляет) данные из входного потока, присваивая значение указанной переменной.
ПЕРВОЕ ЗНАКОМСТВО С ПЕРЕГРУЗКОЙ ФУНКЦИЙ
Перегрузка функций позволяет вашим программам определять несколько функций с одним и тем же именем и типом возвращаемого значения. Например, следующая программа перегружает функцию с именем add_values. Первое определение функции складывает два значения типа int. Второе определение функции складывает три значения. В процессе компиляции C++ корректно определяет функцию, которую необходимо использовать:
#include iostream.h
int add_values(int a,int b)
{
return(a + b);
)
int add_values (int a, int b, int c)
(
return(a + b + c);
)
void main(void)
{
cout "200 + 801 = " add_values(200, 801) endl;
cout "100 + 201 + 700 = " add_values(100, 201, 700) endl;
}
Как видите, программа определяет две функции с именами add_values Первая функция складывает два значения типа int, в то время как вторая складывает три значения. Вы не обязаны что-либо предпринимать специально для того, чтобы предупредить компилятор о перегрузке, просто используйте ее. Компилятор разгадает, какую функцию следует использовать, основываясь на предлагаемых программой параметрах.
Подобным образом следующая программа MSG_OVR.CPP перегружает функцию show_message. Первая функция с именем show_message выводит стандартное сообщение, параметры ей не передаются. Вторая выводит передаваемое ей сообщение, а третья выводит два сообщения:
#include iostream.h
void show_message(void)
{
cout "Стандартное сообщение: " "Учимся программировать на C++" endl;
}
void show_message(char *message)
{
cout message endl;
}
void show_message(char *first, char *second)
{
cout first endl;
cout second endl;
}
void main(void)
{
show_message();
show_message("Учимся программировать на языке C++!");
show_message("B C++ нет предрассудков!","Перегрузка - это круто!") ;
}
ПОЧЕМУ ФУНКЦИИ ОБЫЧНО НЕ МОГУТ ИЗМЕНИТЬ ЗНАЧЕНИЯ ПАРАМЕТРОВ
Следующая программа NOCHANGE.CPP передает два параметра с именами big и small в функцию display_values. Функция display_values, в свою очередь, присваивает обоим параметрам число 1001 и затем выводит значение каждого параметра. Когда функция завершается, программа возобновляется и выводит значения этих же параметров:
#include iostream.h
void display_values(int a, int b)
{ a = 1001;
b = 1001;
cout "Значения в функции display_values равны " а " и " b endl;
}
void main(void)
{
int big = 2002, small = 0;
cout "Значения до функции " big " и" small endl;
display_values(big, small);
cout "Значения после функции " big " и " small endl;
}
Когда вы откомпилируете и запустите эту программу, на экране появится следующий вывод:
С:\ NOCHANGE ENTER
Значения до функции 2002 и 0
Значения в функции display_values равны 1001 и 1001
Значения после функции 2002 и 0
Как видите, значения параметров в функции display_values были изменены (1001). Однако после завершения функции значения переменных big и small в main остались прежними. Чтобы понять, почему изменение параметров не повлияло на переменные big и small в main, вам необходимо понять, как C++ передает параметры в функции.
Когда ваши программы передают параметр в функцию, то по умолчанию С++ делает копию значения параметра и помещает эту копию во временный участок памяти, называемый стеком. Затем функция использует копию значения для выполнения своих операций. Когда функция завершается, C++ сбрасывает содержимое стека и все изменения, сделанные функцией в копиях эначений параметра.
Как вы знаете, переменная представляет собой имя, присваиваемое вашей программой ячейке памяти, которая хранит значение определенного типа. Предположим, например, что переменные big и small находятся в ячейках памяти 10 и 12. Если вы передадите переменные в функцию display_values, C++ поместит копии значений этих переменных в стек. На 10.1 показано, что далее функция display_values будет использовать копии значений переменных.
10.1. C++ размещает копии значений параметров во временном участке памяти, называемом стеком.
Как видите, функция display_values может обращаться к содержимому стека, в котором находятся копии значений 2002 и 0. Так как функция display_values ничего не знает о ячейках памяти big и small (адреса 10 и 12), функция не может изменить реальные значения переменных.
Почему функции C++ обычно не могут изменить значения параметров
Когда вы передаете параметры в функцию, C++ размещает копии значений параметров во временном участке памяти, называемом стеком. Любые изменения, выполняемые функцией над параметрами, проявляются только внутри стека. Когда функция завершается, C++ сбрасывает содержимое стека вместе с любыми изменениями, которые функция произвела в параметрах. Поскольку функция не знает адрес памяти параметра, она не может изменить его значение.
Полиморфизм
Когда программисты говорят о C++ и объектно-ориентированном программировании, то очень часто употребляют термин полиморфизм. В общем случае полиморфизм представляет собой способность объекта изменять форму. Если вы разделите этот термин на части, то обнаружите, что поли означает много, а морфизм относится к изменению формы. Полиморфный объект, следовательно, представляет собой объект, который может принимать разные формы. Этот урок вводит понятие полиморфизма и рассматривает, как использовать полиморфные объекты внутри ваших программ для упрощения и уменьшения кода. К концу данного урока вы освоите следующие основные концепции:
Полиморфизм представляет собой способность объекта изменять форму во время выполнения программы.
C++ упрощает создание полиморфных объектов.
Для создания полиморфных объектов ваши программы должны использовать виртуальные (virtual) функции.
Виртуальная (virtual) функция — это функция базового класса, перед именем которой стоит ключевое слово virtual.
Любой производный от базового класс может использовать или перегружать виртуальные функции.
Для создания полиморфного объекта вам следует использовать указатель на объект базового класса.
ПОСТРОЕНИЕ ИЕРАРХИИ КЛАССОВ
При использовании наследования в C++ для порождения одного класса из другого возможны ситуации, когда вы порождаете свой класс из класса, который уже, в свою очередь, является производным от некоторого базового класса. Например, предположим, вам необходимо использовать класс сотputer базовый для порождения класса workstation, как показано ниже:
class work_station : public computer
{
public:
work_station (char *operating_system, char *name, int hard_disk, float floppy, char *screen, long colors, int x_res, int y_res,int processor, int speed, int RAM);
void show_work_station(void);
private:
char operating_system[64];
};
Конструктор класса workstation просто вызывает конструктор класса computer, который в свою очередь вызывает конструкторы классов сотрuter_screen и mother_board:
work_station::work_station( char *operating_system, char *name, int hard_disk, float floppy, char *screen, long colors, int x_res, int y_res, int processor, int speed, int RAM) : computer (name, hard_disk, floppy, screen, colors, x_res, y_res, processor, speed, RAM)
{
strcpy(work_station::operating_system, operating_system);
}
В данном случае класс computer выступает в роли базового класса. Однако вы знаете, что класс computer был порожден из классов computer_screen и mother_board. В результате класс work_station наследует характеристики всех трех классов. На 27 показано, что порождение классов приводит к иерархии классов.
27. Построение иерархии классов.
Когда ваши программы будут интенсивно использовать наследование, ваша иерархия классов, а следовательно, и количество наследуемых элементов может стать довольно длинной.
Повторение одного или нескольких операторов
Из урока 7 вы узнали, как использовать в ваших программах оператор C++ if для принятия решений. С подобным принятием решений тесно связана способность повторять одну или несколько инструкций определенное число раз или до достижения некоторого условия. В этом уроке вы будете использовать итеративные конструкции C++ для повторения одного или нескольких операторов. К концу данного урока вы освоите следующие основные концепции:
Для повторения операторов определенное число раз ваши программы используют оператор C++ for.
С помощью оператора C++ while программы повторяют операторы до тех пор, пока указанное условие истинно.
Оператор C++ do while позволяет программам выполнять операторы по крайней мере один раз, а затем, возможно, повторять операторы, основываясь на определенном условии.
Возможность повторять операторы очень важна в программировании. Экспериментируйте с программами, представленными в этом уроке. К концу урока вы сможете значительно усовершенствовать свои возможности программирования на C++.
ПОВТОРЕНИЕ ОПЕРАТОРОВ УКАЗАННОЕ ЧИСЛО РАЗ
Одной из наиболее широко используемых операций в ваших программах является повторение одного или нескольких операторов определенное число раз. Например, одна программа могла бы повторять один и тот же оператор, чтобы напечатать пять копий файла, а другая могла бы повторять некоторый Набор операторов 30 раз, чтобы определить, поднялась или упала цена ваших 30 акций. Оператор C++ for предоставляет чрезвычайно простую возможность вашим программам повторять один или несколько операторов указанное число раз.
Если ваша программа использует оператор for (часто называемый циклом for), она должна указать переменную, которая называется управляющей переменной, хранящей количество выполнений цикла. Например, следующий цикл for использует переменную count для хранения количества выполнений цикла. В данном случае цикл будет выполнен десять раз.
for (count = 1; count = 10; count++)
оператор;
Цикл for состоит из четырех частей. Первые три части управляют количеством выполнений цикла. Сначала оператор count = 1, присваивает переменной управления начальное значение. Цикл for выполняет эту инициализацию один раз при запуске цикла. Далее цикл проверяет условие count = 10. Если условие истинно, цикл for выполняет следующий оператор. Если условие ложно, цикл завершается и программа продолжает свое выполнение с первого оператора, следующего за циклом. Если условие истинно и цикл for выполняет свой оператор, то после этого цикл увеличивает переменную count, используя оператор count++. Далее программа проверяет условие count = 10. Если это условие все еще истинно, то опять повторяется выполнение оператора внутри цикла, увеличение и проверка переменной count
for (count = 1; count = 10; count++)
Инициализация Проверка Увеличение
Следующая программа FIRSTFOR. CPP использует цикл for для вывода на экран дисплея значений от 1 до 100:
#include iostream.h
void main(void)
{
int count;
for (count = 1; count =100; count++) cout count ' ';
}
Как видите, оператор for инициализирует переменную count значением 1. Затем цикл проверяет, меньше ли значение переменной count или равно 100. Если это так, цикл for выполняет соответствующий оператор и затем увеличивает count, повторяя проверку. Экспериментируйте с этой программой, изменяя значение 100 на 10, 20 и даже 5000.
Следующая программа ASKCOUNT.CPP выводит сообщение, запрашивающее пользователя ввести число, при котором цикл должен завершиться. Затем программа выводит числа от одного до указанного значения:
#include iostream.h
void main(void)
{
int count;
int ending_value;
cout "Введите конечное значение и нажмите Enter: ";
cin ending_value;
for (count = 0; count = ending_value; count++)
cout count ' ';
}
Экспериментируйте с этой программой, вводя разные числа, например 10, 1 и даже 0. Если вы вводите значение 0 или 1, цикл for никогда не выполняется, потому что условие count = ending_value сразу же ложно. Помните, если вы введете значение вне диапазона значений, которые может хранить переменная типа int, возникнет ошибка переполнения. Например, запустите программу и введите значение 50000. Поскольку это значение превышает наибольшее возможное для переменной типа int, то переполнение приводит к отрицательному значению, которое предотвращает выполнение цикла.
Правила для пропуска значений параметров
Если программа опускает определенный параметр для функции, обеспечивающей значения по умолчанию, то следует опустить и все последующие параметры. Другими словами, вы не можете опускать средний параметр. В случае предыдущей программы, если требовалось опустить значение параметра b в show_parameters, программа также должна была опустить значение параметра с. Вы не можете указать значение для а и с, опуская значение Ь.
Задание значений по умолчанию
Когда вы определяете функцию, C++ позволяет вам указать значения по умолчанию для одного или нескольких параметров. Если программа в дальнейших вызовах этой функции опускает один или несколько параметров, то функция будет использовать для них значения по умолчанию. Чтобы присвоить параметру значение по умолчанию, просто используйте оператор присваивания внутри определения функции.
Например, следующая функция payroll указывает значения по умолчанию для параметров hours и rate:
float payroll(int employ_id, float hours = 40, float rate = 5.50)
{
// операторы
}
Когда программа опускает один параметр, она должна опускать все последующие параметры.
ПРАВИЛА РАБОТЫ СО ССЫЛКАМИ
Ссылка не является переменной. Один раз присвоив значение ссылке, вы уже не можете ее изменить. Кроме того в отличие от указателей вы не можете выполнить следующие операции над ссылками:
• Вы не можете получить адрес ссылки, используя оператор адреса C++.
• Вы не можете присвоить ссылке указатель.
• Вы не можете сравнить значения ссылок, используя операторы сравнения C++.
• Вы не можете выполнить арифметические операции над ссылкой, например добавить смещение.
•Вы не можете изменить ссылку.
По мере использования объектно-ориентированного программирования на C++ вы вернетесь к ссылкам.
Использование ссылок для изменения параметров функции
Из урока 10 вы узнали, что ваши программы с помощью указателей могут изменять значение параметров внутри функции. Для изменения параметра вы должны передать его адрес в функцию. Чтобы получить адрес параметра, используйте оператор адреса C++ (). В свою очередь функция использует переменные-указатели (которые хранят адрес памяти). Для объявления переменной-указателя внутри функции предваряйте имя параметра звездочкой (*). Чтобы изменить или использовать значение параметра внутри функции, предваряйте каждое обращение к имени этого параметра оператором разыменования C++ (*). К сожалению, многие операции внутри функции комбинируют переменные-указатели и переменные-неуказатели.
Ссылки C++ упрощают процесс изменения параметров функции, избавляя от операторов, которые смешивают переменные-указатели и переменные-неуказатели.
ПРЕДСТАВЛЕНИЕ О ДЕСТРУКТОРЕ
Деструктор автоматически запускается каждый раз, когда программа уничтожает объект. В следующих уроках вы узнаете, как создать списки объектов, которые увеличиваются или уменьшаются по мере выполнения программы. Чтобы создать такие динамические списки, ваша программа для хранения объектов распределяет память динамически (что вы еще не научились делать). К настоящему моменту вы можете создавать и уничтожать объекты в процессе выполнения программы. В таких случаях имеет смысл применение деструкторов.
Каждая из созданных вами до сих пор программ создавала объекты в самом начале своего выполнения, просто объявляя их. При завершении программ 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. В своей программе вы определяете деструктор точно так же, как и любой другой метод класса.
ПРЕДСТАВЛЕНИЕ О ГРУППИРУЮЩИХ ОПЕРАТОРАХ { }
По мере усложнения в ваших программах будет один набор операторов, которые компьютер должен выполнить определенное число раз, и другой набор операторов, которые компьютер должен выполнить, если выполняется определенное условие. В первом случае компьютер может выполнить один и тот же набор операторов 100 раз, чтобы добавить для 100 студентов тестовые очки. Во втором случае компьютер может вывести на экран одно сообщение, если все студенты прошли тест, и другое сообщение, если один или несколько студентов потерпели неудачу. Внутри своих программ на C++ вы будете использовать правую и левую фигурные скобки {}, чтобы сгруппировать связанные операторы. В простых программах, представленных в нескольких первых уроках книги, эти символы группируют операторы, которые соответствуют операторам вашей главной программы.
Представление о префиксной (до) и постфиксной (после) операциях увеличения
При использовании операций увеличения ваши программы могут размещать оператор увеличения до или после переменной, как показано ниже:
++variable; variable++;
Так как первый оператор появляется до переменной, он называется префиксным оператором увеличения. Аналогично этому, второй оператор появляется после переменной и называется постфиксным оператором увеличения. Вам необходимо знать, что C++ трактует эти два оператора по-разному. Например, рассмотрим следующий оператор присваивания:
current_count = count++;
Этот оператор присваивания указывает C++ присвоить текущее значение count переменной current_count. В дополнение к этому постфиксный оператор увеличения заставляет C++ увеличить текущее значение count. Использование постфиксного оператора в этом случае делает показанный выше оператор эквивалентным следующим двум операторам:
current_count = count;
count = count + 1;
Теперь рассмотрим следующий оператор присваивания, который использует префиксный оператор увеличения:
current_count = ++count;
В этом случае оператор присваивания указывает C++ сначала увеличить значение count, а затем присвоить результат переменной current_count. Использование префиксного оператора увеличения делает показанный выше оператор эквивалентным следующим двум операторам:
count = count + 1;
current_count = count;
Важно освоить префиксную и постфиксную операции увеличения, так, как они будут встречаться вам в большинстве программ на C++. Следующая программа PRE_POST.CPP иллюстрирует использование префиксной и постфиксной операций увеличения:
#include iostream.h
void main(void)
{
int small_count = 0;
int big_count = 1000;
cout "small_count равно " small_count endl;
cout "small_count++ производит " small_count++ endl;
cout "конечное значение small_count равно " sniall_count endl;
cout "big_count равно " big_count endl;
cout "++big_count производит " ++big_count endl;
cout "конечное значение big_count равно " big_count endl;
}
Когда вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:
С:\ PRE_POST ENTER
small_count равно 0
small_count++ производит 0
конечное значение small_count равно 1
big_count равно 1000
++big_count производит 1001
конечное значение big_count равно 1001
С переменной small_count программа использует постфиксную операцию увеличения. В результате программа выводит текущее значение переменной (0), а затем увеличивает его на 1. С переменной big_count программа использует префиксную операцию увеличения. В результате программа сначала увеличивает значение переменной (1000 + 1), а затем выводит результат (1001). Найдите время, чтобы отредактировать эту программу, и сначала измените постфиксную операцию на префиксную, а затем префиксную на постфиксную. Откомпилируйте и запустите программу, обращая внимание на то, как изменение операции изменяет вывод.
Представление о простых и составных операторах
При использовании оператора if для условной обработки в некоторых случаях, если условие истинно, программе потребуется выполнить один оператор, а в других случаях несколько операторов. Когда программа выполняет только один оператор, следующий за if, такой оператор называется простым оператором:
if (test_score = 90) cout "Поздравляем, вы получили A!" endl; //------- Простой оператор
Если программе необходимо выполнить несколько инструкций, когда результат сравнения — истина, операторы должны быть сгруппированы внутри левой и правой фигурных скобок {}. Операторы, которые находятся внутри фигурных скобок, образуют составной оператор, как показано ниже:
if (test_score = 90)
{
cout "Поздравляем, вы получили A!" endl;
cout "Ваши тестовые очки были " test_Score endl;
} //-----------------Составной оператор
Вам необязательно запоминать термины "простой" и "составной" операторы, но вы должны знать, что следует группировать связанные операторы внутри левой и правой фигурных скобок. Следующая программа COMPOUND.CPP представляет собой измененный вариант предыдущей и выводит два сообщения, если тестовые очки больше или равны 90:
#include iostream.h
void main(void)
{
int test_score = 95;
if (test_score = 90)
{
cout "Поздравляем, вы получили А!" endl;
cout "Ваши тестовые очки были " test_score endl;
}
}
Использование простых и составных операторов
При выполнении условной обработки в некоторых случаях программе необходимо выполнить только один оператор (простой оператор), если условие истинно. Однако в других случаях программа должна будет выполнить несколько операторов (составной оператор). Если в зависимости от результата определенного условия вашей программе нужно выполнить два или несколько связанных операторов, вы должны сгруппировать операторы внутри левой и правой фигурных скобок, как показано ниже:
if (age = 21)
{
cout "Все на выборы!" endl;
cout "Это то, что вам надо!" endl;
}
ПРЕДСТАВЛЕНИЕ О ПРОТОТИПАХ ФУНКЦИЙ
Прежде чем ваша программа сможет вызвать функцию, C++ должен знать тип воозвращаемого значения, а также количество и тип параметров, используемых функцией. В каждой из программ, представленных в этом уроке, определение функции, вызываемой программой, всегда предшествует вызову функции в исходном файле. Однако в большинстве случаев функции появляются в вашем исходном файле и, как правило, одна функция вызывает другую. Чтобы гарантировать, что C++ знает особенности каждой функции, используемой в программе, вы можете поместить прототипы функций в начало исходного файла. В общем случае прототип функции обеспечивает информацию о типе возвращаемого функцией значения и ее параметрах. Следующий оператор иллюстрирует прототипы функций для нескольких функций, используемых в данном уроке:
void show_message(void);
void show_number(int);
void show_employee(int, float);
int add_values(int, int);
float average_value(int, int);
Как видите, прототип функции указывает тип возвращаемого значения, а также количество и тип каждого параметра. Обратите внимание на точку с запятой в конце каждого прототипа.
float average_value (int, int);
Если ваша программа вызывает функцию, для которой компилятор C++ не нашел определения или прототипа, компилятор сообщает о синтаксической ошибке. При исследовании заголовочных файлов C++ или других программ вы будете постоянно сталкиваться с прототипами функций. Следующая программа PROTO.CPP иллюстрирует использование прототипа Функции:
#include iostream.h
float average_value(int, int); // Прототип функции
void main(void)
{
cout "Среднее значение 2000 и 2 равно " average_value(2000, 2) endl;
}
float average_value (int a, int b)
{
return((a + b) / 2.0);
}
В этом случае программа вызывает функцию average_value до того, как функция определена. Таким образом, программа использует прототип функции, который предваряет определение main. Если вы удалите прототип функции и откомпилируете эту программу, компилятор C++ будет сообщать о синтаксических ошибках.
Использование прототипов функций
Прототип функции сообщает компилятору C++ тип возвращаемого значения, а также количество и тип параметров функции. Когда вы компилируете вашу программу, компилятор C++ использует прототип каждой функции, чтобы убедиться, что вы не перепутали тип возвращаемого функцией значения (например, не присваиваете возвращаемое значение типа float переменной типа int) и что вы не передаете в качестве параметра значение неверного типа. Раньше многие компиляторы C++ не выполняли подобную проверку. В результате программисты часто тратили часы, пытаясь найти ошибки, возникающие из-за того, что вместо ожидаемого значения типа float в функцию передавалось значение типа int. Если вы встречаете синтаксическую ошибку, которая возникает из-за противоречия с прототипом функции, будьте благодарны. В прошлом компилятор не определял подобную ошибку, и ваша программа просто не работала.
ПРЕДСТАВЛЕНИЕ О ТОЧНОСТИ
В предыдущем разделе вы узнали, что ошибки переполнения возникают при присваивании переменной значения, которое находится вне диапазона значений для переменной данного типа. Подобно этому вам также необходимо знать, что компьютеры не могут обеспечить неограниченную точность при хранении чисел. Например, при работе с числами с плавающей точкой (числа с десятичной точкой) компьютер не всегда может представить число с требуемой точностью. Поэтому возможны ошибки округления, которые тяжело обнаружить.
Следующая программа PRECISE. CPP присваивает значение чуть меньше 0.5 переменным типа float и double. К сожалению, поскольку компьютер обладает ограниченной способностью в представлении чисел, переменные реально содержат не присваиваемые им значения, а число 0.5:
#include iostream.h
void main(void)
{
float f_not_half = 0.49999990;
double d_not_half = 0.49999990;
cout "Значение типа float 0.49999990 равно " f_not_half endl;
cout "Значение типа double 0.49999990 равно " d_not_half endl;
}
Когда вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:
Значение типа float 0.49999990 равно 0.5
Значение типа double 0.49999990 равно 0.5
Как видите, значения, присваиваемые программой переменным, и значения, которые переменные содержат в действительности, не идентичны. Такие ошибки округления происходят потому, что компьютер должен представлять числа, используя фиксированное количество единиц и нулей. В большинстве случаев компьютер может точно представлять числа. Однако иногда, как показано в этой программе, компьютер представляет числа приближенно, а не точно. При программировании вам необходимо всегда помнить о точности. В зависимости от значений, с которыми работают ваши программы, могут возникать трудно обнаруживаемые ошибки округления.
ПРЕДСТАВЛЕНИЕ ОБ АНОНИМНЫХ ОБЪЕДИНЕНИЯХ C++
Анонимное объединение представляет собой объединение, у которого нет имени. C++ предоставляет анонимные объединения, чтобы упростить использование элементов объединений, предназначенных для экономии памяти или создания псевдонимов для определенного значения. Например, предположим, что вашей программе требуются две переменные miles и meters. Кроме того, предположим, что программа использует только одну из них каждый данный момент времени. В этом случае программа могла бы использовать элементы объединения, подобного уже обсуждавшемуся объединению distance, а именно name.miles и name.meters. Следующий оператор создает анонимное (безымянное) объединение:
union
{
int miles;
long meters;
};
Как видите, объявление не использует имя объединения и не объявляет переменную объединения. Программа, в свою очередь, может обращаться к элементам с именами miles и meters без помощи точки. Следующая программа ANONYM.CPP создает анонимное объединение, которое содержит элементы miles и meters. Обратите внимание, что программа трактует элементы как обычные переменные. Однако различие между элементами и обычными переменными заключается в том, что, когда вы присваиваете значение любому из этих элементов, значение другого элемента теряется:
#include iostream.h
void main(void)
{
union
{
int miles;
long meters;
};
miles = 10000;
cout "Значение в милях " miles endl;
meters = 150000;
cout "Значение в метрах " meters endl;
}
Как видите, с помощью анонимного объединения, программа может сэкономить память, не используя имя объединения и точку для обращения к значениям элементов.
Анонимные объединения позволяют вашим программам экономить пространство
Анонимное объединение представляет собой безымянное объединение. Анонимные объединения обеспечивают вашим программам способ экономии памяти, и при этом можно не использовать имя объединения и точку. Следующие олераторы определяют анонимное объединение, способное хранить две символьные строки:
union
{
char short_name[13];
char long_name[255];
};
ПРЕДСТАВЛЕНИЕ ОБ ОБЪЕКТАХ И ОБЪЕКТНО-ОРИЕНТИРОВАННОМ ПРОГРАММИРОВАНИИ
В известном смысле объект представляет собой сущность. Программа обычно использует переменные для хранения информации о различных реально существующих сущностях, например служащих, книгах и даже файлах. При объектно-ориентированном программировании вы фокусируетесь на предметах, образующих систему, и операциях, которые вы должны выполнять над этими предметами. Например, для объекта-файла вы могли бы иметь операции, которые печатают, отображают или изменяют файл. В C++ вы используете класс для определения своих объектов. Ваша цель состоит в том чтобы включить в класс столько информации об объекте, сколько требуется. Исходя из этого, можно подобрать класс, созданный вами для одной программы, и использовать его в нескольких разных программах.
Класс позволяет вашим программам группировать данные и функции которые выполняют операции над этими данными. Большинство книг и статей об объектно-ориентированном программировании называют функции класса методами. Подобно структуре, класс C++ должен иметь уникальное имя, за которым следует открывающая фигурная скобка, один или несколько элементов и закрывающая фигурная скобка:
class class_name
{
int data_member; // Элемент данных
void show_member(int); // Функция-элемент
};
После определения класса вы можете объявлять переменные типа этого класса (называемые объектами), как показано ниже:
class_name object_one, object_two, object_three;
Следующее определение создает класс employee, который содержит определения данных и метода:
class employee
{
public:
char name[64] ;
long employee_id;
float salary;
void show_employee(void)
{
cout "Имя: " name endl;
cout "Номер служащего: " employee_id endl;
cout "Оклад: " salary endl;
};
};
В данном случае класс содержит три переменные и одну функцию-элемент. Обратите внимание на использование метки public внутри определения класса. Как вы узнаете из урока 22, элементы класса могут быть частными (private) или общими {public), от чего зависит, как ваши программы обращаются к элементам класса. В данном случае все элементы являются общими, это означает, что программа может обращаться к любому элементу, используя оператор точку. После определения класса внутри вашей программы вы можете объявить объекты (переменные) типа этого класса, как показано ниже:
————————————————————— Имя класса
employee worker, boss, secretary; //---------Переменные класса (объекты)
Следующая программа EMPCLASS.CPP создает два объекта employee. Используя оператор точку, программа присваивает значения элементам данных Затем программа использует элемент show_employee для вывода информации о служащем:
#include iostream.h
#include string.h
class employee
{
public:
char name [64];
long employee_id;
float salary;
void show_employee(void)
{
cout "Имя: " name endl;
cout "Номер служащего: " employee_id endl;
cout "Оклад: " salary endl;
};
};
void main(void)
{
employee worker, boss;
strcpy(worker.name, "John Doe");
worker.employee_id = 12345;
worker.salary = 25000;
strcpy(boss.name, "Happy Jamsa");
boss.employee_id = 101;
boss.salary = 101101.00;
worker.show_employee();
boss.show_employee();
}
Как видите, программа объявляет два объекта типа employee — worker и boss, а. затем использует оператор точку для присваивания значений элементам и вызова функции show_employee.
Представление об объектах
Большинство программ на C++ отражают реальные существующие объекты. В известном смысле объекты представляют собой сущности, например автомобиль, собаку, часы и т. д. Обычно объект имеет несколько атрибутов и операций, которые программа должна выполнять над этими атрибутами. Например, в случае часов свойства могут включать текущее время и время будильника. Операции такого объекта могли бы содержать установку времени, установку будильника или выключение будильника. При объектно-ориентированном программировании ваши программы фокусируются на объектах и операциях над этими объектами.
Представление об области видимости переменных
При чтении книг и журнальных статей по C++ вы можете встретить термин область видимости, что определяет участок в программе, где имя переменной имеет смысл (и, следовательно, используется). Для локальной переменной область видимости ограничена функцией, внутри которой эта переменная объявлена. С другой стороны, глобальные переменные известны на протяжении всей программы. В результате глобальные переменные имеют большую область видимости.
ПРЕДСТАВЛЕНИЕ ОБ ОПЕРАТОРЕ #include
Каждая программа, представленная в уроке 1, начинается со следующего оператора #include:
#include iostream.h
При создании программ на C++ вы получаете преимущества от использования операторов и определений, которые обеспечивает вам компилятор. При компиляции программы оператор #include заставляет компилятор включить содержимое заданного файла в начало вашей программы. В данном случае компилятор включит содержимое файла iostream.h.
Файлы с расширением h, которые вы включаете в начало (или заголовок) вашей программы, называются заголовочными файлами. Если вы посмотрите на каталог, содержащий файлы вашего компилятора, то найдете подкаталог с именем INCLUDE, в котором находятся разные заголовочные файлы. Каждый заголовочный файл содержит определения, предоставляемые компилятором для различных операций. Например, существует заголовочный файл, который содержит определения для математических операций, другой заголовочный файл описывает файловые операции и т. д.
Заголовочные файлы представляют собой файлы в формате ASCII, следовательно, вы можете вывести их содержимое на экран или принтер. В данный момент не беспокойтесь о содержимом заголовочных файлов. Просто поймите, что оператор #include позволяет вам использовать эти файлы. Все программы на C++, созданные вами в процессе изучения этой книги, содержат операторы #include, которые вы должны применять в ваших программах.
Заголовочные файлы C++
Каждая создаваемая вами программа на C++ начинается с одного или нескольких операторов #include. Эти операторы указывают компилятору включить содержимое заданного файла (заголовочного файла) в вашу программу, как если бы программа содержала операторы, которые находятся во включаемом файле. Заголовочные файлы содержат определения, используемые компилятором для операций различных типов. Существуют заголовочные файлы, которые определяют операции В/В (ввода/вывода) C++, системные функции (например, функции, возвращающие текущие дату и время) и многое другое.
Заголовочные файлы, подобно программам на C++, представляют собой файлы в формате ASCII, содержимое которых вы можете просмотреть или напечатать. Чтобы лучше понять содержимое заголовочных файлов, найдите время для того, чтобы напечатать заголовочный файл IOSTREAM.H, содержимое которого вы будете использовать в каждой создаваемой вами программе на C++. Обычно заголовочный файл IOSTREAM.H расположен в подкаталоге с именем INCLUDE, который находится в каталоге, содержащем файлы компилятора C++. Используйте текстовый редактор, чтобы просмотреть и напечатать содержимое заголовочных файлов.
Замечание: Никогда не изменяйте содержимое заголовочных файлов. Это может привести к ошибкам компиляции в каждой создаваемой вами программе.
Преимущества использования библиотеки этапа выполнения
Из урока 9 вы узнали, как разделить ваши программы на небольшие легко управляемые части, называемые функциями и выполняющие определенную задачу. Одно из преимуществ использования функций заключается в том, что вы можете часто применяемую функцию, созданную для одной программы, использовать в другой программе. Как вы узнаете из этого урока, большинство компиляторов C++ обеспечивают широкий набор функций, использующихся в программах и называющихся библиотекой этапа выполнения. Применение этих функций сокращает объем программирования, который вы должны выполнить самостоятельно. Вместо этого ваша программа просто вызывает функции библиотеки этапа выполнения. В зависимости от компилятора библиотека этапа выполнения может состоять из тысяч функций. В данном уроке описывается использование таких функций в ваших программах. К тому времени, когда вы закончите этот урок, вы освоите следующие основные концепции:
• Библиотека этапа выполнения представляет собой набор функций, обеспечиваемых вашим компилятором, которые вы можете легко использовать в программах.
• Для использования функций библиотеки этапа выполнения вы должны включить соответствующие заголовочные файлы, содержащие прототипы функций.
• Некоторые компиляторы обращаются к библиотеке этапа выполнения как к интерфейсу прикладных программ или API.
Большинство библиотек этапа выполнения содержат сотни прикладных функций, которые помогут вам сохранить огромное количество времени и быстро разработать сложные программы. Вы узнаете, что очень легко использовать функции библиотеки этапа выполнения!
Как вы уже знаете, символ
Как вы уже знаете, символ NULL представляет собой символ ASCII 0. В уроке 7 вы изучали, что C++ использует значение 0, чтобы представлять ложь. Таким образом, поскольку символ NULL равен 0, ваши программы могут упростить многие операции цикла. Например, многие функции просматривают символьные строки символ за символом в поиске NULL. Следующий цикл for иллюстрирует, как программа может искать NULL в строке:
for (index = 0; string[index] != NULL; index++)
Поскольку символ NULL равен 0, многие программы упрощают циклы, которые ищут NULL, как показано ниже:
for (index = 0; string[index]; index++);
В данном случае пока символ, содержащийся в string[index] не NULL (0 или ложь), цикл продолжается.
ПРЕВЫШЕНИЕ ДИАПАЗОНА ЗНАЧЕНИЙ ПЕРЕМЕННОЙ
Как вы уже знаете, тип переменной определяет набор значений, которые переменная может хранить. Например, переменная типа int может хранить значения в диапазоне от -32768 до 32767. Если вы присваиваете переменной значение, которое находится вне этого диапазона, возникает ошибка переполнения. Например, следующая программа OVERFLOW.CPP иллюстрирует, как превышение диапазона значений переменной приводит к ошибке. Как видите, программа присваивает значения, которые находятся вне диапазона для переменной каждого типа:
#include iostream.h
void main(void)
{
int positive = 40000;
long big_positive = 4000000000;
char little_positive = 210;
cout "сейчас positive содержит " positive endl;
cout "сейчас big_positive содержит " big_positive endl;
cout "сейчас little_poaitive содержит " little_positive endl;
}
Когда вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:
сейчас positive содержит -25536
сейчас big_positive содержит -294967296
сейчас little_positive содержит Т
Как видите, программа присваивает переменным типа int, long и char значения, которые находятся вне диапазона хранения каждого типа, поэтому возникает ошибка переполнения. При работе с переменными вам необходимо помнить диапазон значений, которые может хранить переменная каждого типа. Ошибки переполнения плохо уловимы, и поэтому их трудно определить и исправить. Обратите внимание на значение, которое программа выводит для переменной little_positive. Поскольку это переменная типа char, выходной поток cout пытается вывести ее значение в символьном виде. В этом случае выведенное значение соответствует второй половине таблицы ASCII со значением 210.
ПРИМЕНЕНИЕ СОСТАВНЫХ ОПЕРАТОРОВ ДЛЯ else
Как вы уже знаете, составной оператор представляет собой группу связанных операторов, заключенных между левой и правой фигурными скобками. Когда программа использует else для указания операторов, выполняющихся, если условие ложно, то для указания нескольких операторов можно использовать составной оператор. Следующая программа CMP_ELSE.CPP использует составные операторы как для if, так и для else:
#include iostream.h
void main(void)
{
int test_score = 65;
if (test_score = 90)
{
cout " Поздравляю, вы получили A!" endl;
cout "Ваши тестовые очки были " test_score endl;
}
else
{
cout "Вы должны работать усерднее!" endl;
cout "Вы потеряли " 100 - test_score " очков " endl;
}
}
Как и ранее, найдите время поэкспериментировать с этой программой, изменяя значения переменной test_score так, чтобы оно было меньше или больше 90. Следующая программа GETSCORE.CPP использует входной поток cin для ввода тестовых очков пользователем. Затем программа сравнивает тестовые очки со значением 90, выводя соответствующее сообщение:
#include iostream.h
void main(void)
{
int test_score;
cout "Введите тестовые очки и нажмите Enter: ";
cin test_score;
if (test_score = 90)
{
cout "Поздравляем, вы получили А!" endl;
cout "Ваши тестовые очки были " test_score endl;
}
else
{
cout "Вы должны работать усерднее!" endl;
cout "Вы потеряли " 100 - test_score " очков " endl;
}
}
Откомпилируйте и запустите эту программу. Вы увидите, что при комбинации операций ввода с условной обработкой ваши программы станут более совершенными.
Представление об if-else
По мере усложнения ваши программы будут проверять разные условия и выполнять один набор операторов, если условие истинно, и другой набор, если условие ложно. Для выполнения такой условной обработки программы используют операторы if-else, как показано ниже:
if (условие_истинно)
оператор;
else
оператор;
Когда программе требуется выполнить несколько операторов, если условие ложно или истинно, вы должны сгруппировать связанные операторы внутри левой и правой фигурных скобок {}:
if (условие_истинно)
{
первый_оператор_для_истины;
второй_оператор_для_истины;
}
else
{
первый_оператор_для_лжи;
второй_оператор_для_лжи;
}
ПРИСВАИВАНИЕ ЗНАЧЕНИЯ ПЕРЕМЕННОЙ
Как вы уже знаете, переменные хранят значения во время выполнения программы. После объявления переменной вы используете оператор присваивания C++ (знак равно), чтобы присвоить значение переменной. Следующие операторы присваивают значения нескольким разным переменным. Обратите внимание на использование точки с запятой в конце каждого оператора:
age = 32;
salary = 25000.75;
distance_to_the_moon = 238857;
Замечание: Значения, присваиваемые переменным, не должны содержать запятые (например, 25,000.75 и 238,857) *. Если вы включаете запятые, компилятор C++ будет генерировать и выводить сообщения о синтаксических ошибках.
Фрагмент следующей программы сначала объявляет переменные, а затем использует оператор присваивания, чтобы присвоить переменным значения:
*Американская запись чисел предполагает отделение каждых трех разрядов целой части числа запятой. - Прим. ред.
#include iostream.h
void main(void)
{
int age;
float salary;
long distance_to_the_moon;
age = 32;
salary = 25000.75;
distance_to_the_moon = 238857;
}
Присваивание значения при объявлении
При объявлении переменной часто удобно присваивать ей начальное значение. Чтобы упростить такую процедуру, C++ позволяет присваивать значение во время объявления переменной, как показано ниже:
int age = 32;
float salary = 25000.75;
long distance_to_the_moon = 238857;
Многие программы, представленные в этой книге, присваивают значения переменным при объявлении.
Присваивание значений переменным
При выполнении программы переменные хранят информацию. Для записи значения в переменную программы должны использовать оператор присваивания C++ (знак равно). Следующая строка использует оператор присваивания для записи значения 4 в переменную lesson:
lesson = 4;
Для упрощения этого процесса в C++ можно также присвоить значение переменной при ее объявлении:
int lesson = 4;
ПРОГРАММА МОЖЕТ ПЕРЕДАВАТЬ ИНФОРМАЦИЮ В ФУНКЦИИ
Для увеличения потенциальных возможностей ваших функций C++ позволяет программам передавать информацию (параметры) в функции. Если функция использует параметры, вы должны сообщить C++ тип каждого параметра, например int, float, char и т.д. Следующая функция show_number использует параметр типа int.
void show_number (int value)
{
cout "Значение параметра равно " value endl;
}
Если ваша программа вызывает функцию show_number, она должна передать ей значение, как показано ниже:
show_number (/u>); //---------------- Значение, передаваемое в функцию
C++ будет подставлять переданное число вместо каждого имени параметра value внутри функции:
show_number (1001)
void show_number (int value)
{
cout "Значение параметра равно " value endl;
}
void show_number (1001)
{
cout "Значение параметра равно " 1001 endl;
}
Как видите, поскольку C++ замещает значение параметра, функция show_number выводит число 1001, переданное ей главной программой.
Следующая программа USEPARAM.CPP использует функцию show_number несколько раз, каждый раз передавая разные числа:
#include iostream.h
void show_number (int value)
{
cout "Значение параметра равно " value endl;
}
void main (void)
{
show_number (1);
show_number (1001);
show_number (-532);
}
Если вы откомпилируете и запустите эту программу, на вашем экране будет отображено следующее:
С: \USEPARAM ENTER
Значение параметра равно 1
Значение параметра равно 1001
Значение параметра равно -532
Как видите, каждый раз, когда программа вызывает функцию, C++ присваивает передаваемое число переменной value. Найдите время для эксперимента с этой программой, изменяя значения, которые main передает в функцию, и обращая внимание на результат.
Каждый параметр функции имеет определенный тип. В случае функции show_number параметр value должен быть типа int. Если вы попытаетесь передать в функцию значение другого типа, например с плавающей точкой, компилятор будет сообщать об ошибке. В большинстве случаев ваши программы будут передавать несколько значений в функцию. Для каждого передаваемого параметра функция должна указать имя и тип. Например, следующая программа BIGSMALL.CPP использует функцию show_big_and_little для вывода самого большого и самого маленького из трех полученных целочисленных значений:
#include iostream.h
void show_big_and_little (int a, int b, int c)
{
int small = a;
int big = a;
if (b big)
big = b;
if (b small)
small = b;
if (c big)
big = c;
if (c small)
small = c;
cout "Самое большое значение равно " big endl;
cout "Самое маленькое значение равно " small endl;
}
void main (void)
{
show_big_and_little (1, 2, 3);
show_big_and_little (500, 0, -500);
show_big_and_little (1001, 1001, 1001);
}
Если вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:
С: \ BIGSMALL ENTER
Самое большое значение равно 3
Самое маленькое значение равно 1
Самое большое значение равно 500
Самое маленькое значение равно -500
Самое большое значение равно 1001
Самое маленькое значение равно 1001
Наконец, следующая программа SHOW_EMP.CPP использует функцию show_employee для вывода возраста (тип int) и оклада (тип float) служащего:
#Include iostream.h
void show_employee (int age, float salary)
{
cout "Возраст служащего " age " года (лет)" endl;
cout "Служащий получает $" salary endl;
}
void main (void)
{
show_employee (32, 25000.00);
}
Как видите, функция show_employee определяет параметры типа int и float
Передача параметров в функцию
Если ваша функция использует параметры, она должна указать уникальное имя и тип для каждого параметра. Когда программа вызывает функцию, C++ присваивает значения параметров именам параметров функции слева направо. Каждый параметр функции имеет определенный тип, например int, float или char. Значения, которые ваша программа передает в функцию, используя параметры, должны соответствовать типу параметров.
Программа принимает решение
Как вы уже знаете, программа представляет собой последовательность инструкций, выполняемых компьютером для реализации определенных задач. Все созданные вами до сих пор простые программы на C++ выполняли операторы по порядку, начиная с первого до конца программы. По мере усложнения программ вам потребуется, чтобы выполнялся один набор операторов, если определенное условие соблюдается, и другой набор, если условие не соблюдается. Другими словами, вам потребуется, чтобы ваши программы приняли решение и соответственно отреагировали. В этом уроке описывается оператор C++ if, который будет использоваться вашей программой для принятия подобных решений. К концу данного урока вы освоите следующие основные концепции:
Программы на C++ используют операции сравнения, чтобы определить, равны ли два значения или одно значение больше или меньше другого.
Для принятия решений используется оператор C++ if.
Операторы C++ могут быть простыми (одна операция) или составными (несколько операций, сгруппированных внутри правой и левой фигурных скобок {}).
Ваши программы используют оператор C++ if-else для выполнения одного набора операторов, если определенное условие соблюдается, и другого набора операторов, если условие не соблюдается.
Комбинируя несколько операторов if-else, программы могут проверять несколько условий.
Используя логические операторы C++ И и ИЛИ, ваши программы могут проверить несколько условий, например: Есть ли у пользователя собака И долматин ли это ?
Программы, которые принимают решения, выполняют условную обработку. Другими словами, на основании результата одного или нескольких Условий программа будет выполнять определенные операторы. Экспериментируйте с программами данного урока; ваш набор инструментальных средств C++ уже достаточно обширен для создания полезных программ.
Программы хранят информацию в переменных
Все программы, представленные в уроках 1—3, были очень простыми. Однако по мере того, как ваши программы начинают выполнять более многоплановые задачи, они должны хранить информацию во время выполнения. Например, программе, печатающей файл, нужно знать имя файла и, возможно, число копий, которые вы хотите напечатать. В процессе выполнения программы хранят такую информацию в памяти компьютера. Чтобы использовать определенные ячейки памяти, программы применяют переменные. Проще говоря, переменная представляет собой имя ячейки памяти, которая может хранить конкретное значение. В этом уроке описано, как создавать и использовать переменные в программах на C++. К концу данного урока вы освоите следующие основные концепции:
Вы должны объявлять переменные, которые будете использовать в программе, сообщая компилятору имя и тип переменной.
Тип переменной определяет тип значения (например, целое число или число с плавающей точкой), которое может хранить переменная, а также операции, которые ваши программы могут выполнять над переменной.
Чтобы присвоить значения переменной, используйте оператор присваивания C++ (знак равно).
Для вывода значения переменной на экран программы используют выходной поток cout.
При объявлении переменных используйте имена, выражающие смысл переменных, чтобы ваши программы было легче читать и понимать.
Используйте комментарии, описывающие работу программы. В этом случае, если другим программистам нужно изменить вашу программу, комментарии подробно опишут работу программы.
Когда вы присваиваете значение переменной, представьте себе переменную в виде ящика, в который можно поместить значение. Если вам позже потребуется использовать значение переменной, компьютер просто посмотрит значение, содержащееся в ящике.
ПРОСТОЕ НАСЛЕДОВАНИЕ
Наследование представляет собой способность производного класса наследовать характеристики существующего базового класса. Например, предположим, что у вас есть базовый класс 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 из базового класса employee. Порождая класс manager из существующего класса employee, вы снижаете объем требуемого программирования и исключаете дублирование кода внутри вашей программы.
Для определения этого класса вы должны указать ключевое слово class, имя manager, следующее за ним двоеточие и имя employee, как показано ниже:
Производный класс //----- class manager : public employee { -------// Базовый класс
// Здесь определяются элементы
};
Ключевое слово public, которое предваряет имя класса employee, указывает, что общие (public) элементы класса employee также являются общими и в классе manager. Например, следующие операторы порождают класс manager.
class manager : public employee
{
public:
manager(char *, char *, char *, float, float, int);
void show_manager(void);
private:
float annual_bonus;
char company_car[64];
int stock_options;
};
Когда вы порождаете класс из базового класса, частные элементы базового класса доступны производному классу только через интерфейсные функции базового класса. Таким образом, производный класс не может напрямую обратиться к частным элементам базового класса, используя оператор точку.
Следующая программа MGR_EMP.CPP иллюстрирует использование наследования в C++ , создавая класс manager из базового класса employee:
#include iostream.h
#include string.h
class employee
{
public:
employee(char *, char *, float);
void show_employee(void);
private:
char name [ 64 ];
char position[64];
float salary;
};
employee::employee(char *name, char *position,float salary)
{
strcpy(employee::name, name);
strcpy(employee::position, position);
employee::salary = salary;
}
void employee::show_employee(void)
{
cout "Имя: " name endl;
cout "Должность: " position endl;
cout "Оклад: $" salary endl;
}
class manager : public employee
{
public:
manager(char *, char *, char *, float, float, int);
void show_manager(void);
private:
float annual_bonus;
char company_car[64];
int stock_options;
};
manager::manager(char *name, char *position, char *company_car, float salary, float bonus, int stock_options) : employee(name, position, salary)
{
strcpy(manager::company_car, company_car) ;
manager::annual_bonus = bonus ;
manager::stock_options = stock_options;
}
void manager::show_manager(void)
{
show_employee();
cout "Машина фирмы: " company_car endl;
cout "Ежегодная премия: $" annual_bonus endl;
cout "Фондовый опцион: " stock_options endl;
}
void main(void)
{
employee worker("Джон Дой", "Программист", 35000);
manager boss("Джейн Дой", "Вице-президент ", "Lexus", 50000.0, 5000, 1000);
worker.show_employee() ;
boss.show_manager();
}
Как видите, программа определяет базовый класс employee, а затем определяет производный класс manager. Обратите внимание на конструктор manager. Когда вы порождаете класс из базового класса, конструктор производного класса должен вызвать конструктор базового класса. Чтобы вызвать конструктор базового класса, поместите двоеточие сразу же после конструктора производного класса, а затем укажите имя конструктора базового класса с требуемыми параметрами:
manager::manager(char *name, char *position, char *company_car, float salary, float bonus, int stock_options) :
employee(name, position, salary) //————————————— Конструктор базового класса
{
strcpy(manager::company_car, company_car);
manager::annual_bonus = bonus;
manager::stock_options = stock_options;
}
Также обратите внимание, что функция show_manager вызывает функцию show_employee, которая является элементом класса employee. Поскольку класс manager является производным класса employee, класс manager может обращаться к общим элементам класса employee, как если бы все эти элементы были определены внутри класса manager,
Представление о наследовании
Наследование представляет собой способность производного класса наследовать характеристики существующего базового класса. Простыми словами это означает, что, если у вас есть класс, чьи элементы данных или функции-элементы могут быть использованы новым классом, вы можете построить этот новый класс в терминах существующего (или базового) класса. Новый класс в свою очередь будет наследовать элементы (характеристики) существующего класса. Использование наследования для построения новых классов сэкономит вам значительное время и силы на программирование. Объектно-ориентированное программирование широко использует наследование, позволяя вашей программе строить сложные объекты из небольших легко управляемых объектов.
ПРОСТОЙ ПРИМЕР
Предположим, к примеру, у вас есть класс computer_screen:
class computer_screen
{
public:
computer_screen(char *, long, int, int);
void show_screen(void);
private:
char type[32] ;
long colors;
int x_resolution;
int y_resolution;
};
Предположим, что у вас есть также класс mother_board:
class mother_board
{
public:
mother_board(int, int, int);
void show_mother_board(void);
private:
int processor;
int speed;
int RAM;
};
Используя эти два класса, можно породить класс computer, что показано ниже:
class computer : public computer_screen, public mother_board
{
public:
computer(char *, int, float, char *, long, int, int, int, int, int);
void show_computer(void);
private:
char name[64];
int hard_disk;
float floppy;
};
Как видите, этот класс указывает свои базовые классы сразу после двоеточия, следующего за именем класса computer.
class computer : public computer_screen, public mother_board //—————— Базовые классы
Следующая программа COMPUTER. CPP порождает класс computer, используя базовые классы computer_screen и mother_board:
#include iostream.h
#include string.h
class computer_screen
{
public:
computer_screen(char *, long, int, int);
void show_screen(void);
private:
char type[32];
long colors;
int x_resolution;
int y_resolution;
};
computer_screen::computer_screen(char *type, long colors, int x_res, int y_ree)
{
strcpy(computer_screen::type, type);
computer_screen::colors = colors;
computer_screen::x_resolution = x_res;
computer_screen::y_resolution = y_res;
}
void computer_screen::show_screen(void)
{
cout "Тип экрана: " type endl;
cout "Цветов: " colors endl;
cout "Разрешение: " x_resolution " на " y_resolution endl;
}
class mother_board
{
public:
mother_board(int, int, int);
void show_mother_board(void);
private:
int processor;
int speed;
int RAM;
};
mother_board::mother_board( int processor, int speed, int RAM)
{
mother_board::processor = processor;
mother_board::speed = speed;
mother_board::RAM = ram;
}
void mother_board::show_mother_board(void)
{
cout "Процессор: " processor endl;
cout "Частота: " speed "МГц" endl;
cout "ОЗУ: " RAM " МВайт" endl;
}
class computer : public computer_screen, public mother_board
{
public:
computer(char *, int, float, char *, long, int, int, int, int, int);
void show_computerf void);
private:
char name [64];
int hard_disk;
float floppy;
};
computer::computer(char *name, int hard_disk, float floppy, char *screen, long colors, int x_res, int y_res, int processor, int speed, int RAM) : computer_screen(screen, colors, x_res, y_res), mother_board(processor, speed, ram)
{
strcpy(computer::name, name);
computer::hard_disk = hard_disk;
computer::floppy = floppy;
}
void computer::show_computer(void)
{
cout "Тип: " name endl;
cout "Жесткий диск: " hard_disk "МВайт" endl;
cout "Гибкий диск: " floppy "МВайт" endl;
show_mother_board();
show_screen();
}
void main(void)
{
computer my_pc("Compaq", 212, 1.44, "SVGA", 16000000, 640, 480, 486, 66, 8);
my_pc.show_computer();
}
Если вы проанализируете конструктор класса computer, то обнаружите, что он вызывает конструкторы классов mother_board и computer_screen, как показано ниже:
computer::computer(char *name, int hard_disk, float floppy, char *screen, long colors, int x_res, int y_res, int processor, int speed, int RAM) : computer_screen(screen, colors, x_res, y_res), mother_board(processor, speed, RAM)
ПРОВЕРКА ДВУХ ИЛИ БОЛЕЕ УСЛОВИЙ
Как вы уже знаете, оператор if позволяет программам проверять определенные условия. По мере усложнения ваших программ возникает необходимость в проверке сразу нескольких условий. Например, программа могла бы проверить, будут ли тестовые очки больше или равны 90, и получит ли студент оценку А. Подобно этому, вы могли бы проверить, есть ли у пользователя собака и долматин ли это. Для выполнения таких проверок можно использовать логическую операцию C++ И (). Кроме того, если вы хотите проверить, есть ли у пользователя собака или кошка, вам следует использовать логическую операцию ИЛИ (||). Если программы для проверки нескольких условий используют логические операции И или ИЛИ, поместите каждое условие внутри круглых скобок, как показано ниже:
if ((user_owns_a_dog) (dog == dalmatian)) //-------------Полное условие
Как видите, программа группирует каждое условие внутри своих собственных круглых скобок, которые затем заключаются во внешние круглые скобки.
if ((user_owns_a_dog) (dog == dalmatian))
Когда ваша программа использует логическую операцию И (), то результат полного условия является истинным, только если все проверяемые условия истинны. Если какое-либо условие ложно, то полное условие становится ложным. Например, если у пользователя нет собаки, то предыдущее условие является ложным. Подобно этому, если собака пользователя не долматин, условие является ложным. Чтобы условие было истинным, у пользователя должна быть собака и она должна быть породы долматин. Следующий оператор использует логическую операцию ИЛИ (||), чтобы определить, есть ли у пользователя собака или кошка:
if ((user_owns_a_dog) || (user_owns_a_cat))
При использовании логической операции ИЛИ полное условие будет истинным, если хотя бы одно условие является истинным. Например, если у пользователя есть собака, условие истинно. Если у пользователя есть кошка, условие истинно. Подобно этому, если у пользователя есть и собака, и кошка, условие истинно. Условие будет ложным только в том случае, если у пользователя нет ни собаки, ни кошки.
ПРОВЕРКА ОШИБОК ПРИ ВЫПОЛНЕНИИ ФАЙЛОВЫХ ОПЕРАЦИЙ
Программы, представленные до настоящего момента, предполагали, что во время файловых операций В/В не происходят ошибки. К сожалению, это сбывается не всегда. Например, если вы открываете файл для ввода, ваши программы должны проверить, что файл существует. Аналогично, если ваша программа пишет данные в файл, вам необходимо убедиться, что операция прошла успешно (к примеру, отсутствие места на диске, скорее всего, помешает записи данных). Чтобы помочь вашим программам следить за ошибками, вы можете использовать функцию fail файлового объекта. Если в процессе файловой операции ошибок не было, функция возвратит ложь (0). Однако, если встретилась ошибка, функция fail возвратит истину. Например, если программа открывает файл, ей следует использовать функцию fail, чтобы определить, произошла ли ошибка, как это показано ниже:
ifstream input_file("FILENAME.DAT");
if (input_file.fail())
{
cerr "Ошибка открытия FILENAME.EXT" endl;
exit(1);
}
Таким образом, программы должны убедиться, что операции чтения и записи прошли успешно. Следующая программа TEST_ALL.CPP использует функцию fail для проверки различных ошибочных ситуаций:
#include iostream.h
#include fstream.h
void main(void)
{
char line[256] ;
ifstream input_file("BOOKINFO.DAT") ;
if (input_file.fail()) cerr "Ошибка открытия BOOKINFO.DAT" endl;
else
{
while ((! input_file.eof()) (! input_file.fail()))
{
input_file.getline(line, sizeof(line)) ;
if (! input_file.fail()) cout line endl;
}
}
}
РАБОТА В СРЕДЕ ТИПА WINDOWS
Для упрощения в каждом из предыдущих примеров предполагается, что вы работаете в среде, основанной на командной строке, например MS-DOS или UNIX. Однако сегодня большинство программистов на C++ программируют в среде типа Windows, такой как Visual C++, или интегрированной среде разработки фирмы Borland. При программировании в среде типа Windows операторы программы не отличаются от тех, которые показаны здесь. Другими словами, операторы C++ в программе FIRST.CPP, написанной в Windows, идентичны тем, которые вы будете использовать в среде, основанной на командной строке. Что изменяется в Windows, так это процесс компиляции и запуска программы.
например, иллюстрирует среду программирования Windows. Внутри такой среды программирования вы можете создавать исходные файлы, используя встроенный редактор, и затем компилировать программу с помощью выбора пункта меню или щелчка мыши по кнопке инструментальной линейки. Если программа содержит синтаксические ошибки, среда программирования сводит сообщения об ошибках в специальное окно. После того как вы успешно откомпилируете вашу программу, можно использовать пункт меню (или кнопку инструментальной линейки) для запуска программы. Программная среда может открыть отдельное окно, в котором будет отображаться вывод программы.
Среда программирования Windows.
Среда программирования называется так, потому что обеспечивает все инструментальные средства, необходимые для создания, компиляции и запуска программ.
Расширенные возможности C++
В первых пяти частях этой книги были представлены C++ и концепции объектно-ориентированного программирования, которые вам необходимо знать для создания мощных программ. В этой части вы расширите ваши знания о C++ сведениями о распределении памяти во время выполнения программ и знаниями о том, как выполнять файловые операции в C++. Уроки этой части представлены как "Расширенные возможности C++", но не позволяйте им запугать вас. Вы обнаружите, что концепции, представленные в этой части, не сложнее тех, которые вы уже освоили.
Урок 31. Использование свободной памяти в C++.
Урок 32. Управление свободной памятью.
Урок 33. Дополнительные возможности cin и cout.
Урок 34. Файловые операции В/В в C++.
Урок 35. Встроенные функции и ассемблерные коды.
Урок 36. Использование аргументов командной строки.
Урок 37. Использование констант и макрокоманд.
Урок 38. Полиморфизм.
Урок 39. Использование исключительных
ситуаций C++ для обработки ошибок.
Предыдущая часть | Содержание
Рассмотрим второй пример
В уроке 10 вы использовали следующую функцию для перестановки двух значений с плавающей точкой:
void swap_values(float *a, float *b)
{
float temp;
temp = *a;
*a = *b;
*b = temp;
}
Как видите, функция комбинирует переменные-указатели с переменными-неуказателями. Следующая программа SWAP_REF.CPP использует ссылки на значения с плавающей точкой для упрощения функции:
#include iostream.h
void swap_values(float a, float b)
{ float temp;
temp = a;
a = b;
b = temp;
}
void main(void)
{ float big = 10000.0;
float small = 0.00001;
float big_alias = big;
float small_alias = small;
swap_values(big_alias, small_alias);
cout "Big содержит " big endl;
cout "Small содержит " small endl;
}
Как видите, функцию swap_values сейчас легче понять, однако ваша программа имеет теперь два дополнительных имени (ссылки big_alias и small_alias), за которыми вы должны следить.
РАЗРЕШЕНИЕ КОНФЛИКТА ИМЕН
Если вы порождаете один класс из другого, возможны ситуации, когда имя элемента класса в производном классе является таким же, как имя элемента в базовом классе. Если возник такой конфликт, C++ всегда использует элементы производного класса внутри функций производного класса. Например, предположим, что классы book и library_card используют элемент price. В случае класса book элемент price соответствует продажной цене книги, например $22.95. В случае класса library'_card price может включать библиотечную скидку, например $18.50. Если в вашем исходном тексте не указано явно (с помощью оператора глобального разрешения), функции класса library_card будут использовать элементы производного класса {library_card). Если же функциям класса library_card необходимо обращаться к элементу price базового класса {book), они должны использовать имя класса book и оператор разрешения, например book::price. Предположим, что функции show_card необходимо вывести обе цены. Тогда она должна использовать следующие операторы:
cout "Библиотечная цена: $" price endl;
cout "Продажная цена: $" book::price endl;
С++ обеспечивает также операции уменьшения
Как вы уже знаете, двойной знак плюс (++) представляет собой оператор увеличения C++. Подобным образом двойной знак минус (--) соответствует оператору уменьшения C++, который уменьшает значение переменной на 1. Как и в случае с операцией увеличения, C++ поддерживает префиксный и постфиксный операторы уменьшения. Следующая программа DECCOUNT.CPP иллюстрирует использование оператора уменьшения C++:
#include iostream.h
void main(void)
{
int small_count = 0;
int big_count = 1000;
cout "small_count равно " small_count endl;
cout "small_count-- производит " small_count-- endl;
cout "конечное значение small_count равно " “ small_count endl;
cout "big_count равно " big_count endl;
cout "--big_count производит " --big_count endl;
cout "конечное значение big_count равно " big_count endl;
}
Когда вы откомпилируете и запустите эту программу, на вашем экране появится следующий вывод:
С:\ DECCOUNT ENTER”
small_count равно 0
small_count-- производит 0
конечное значение small_count равно -1
big_count равно 1000
—big_count производит 999
конечное значение big_count равно 999
Как видите, префиксный и постфиксный операторы уменьшения C++ работают так же, как и соответствующие операторы увеличения, с той лишь разницей, что они уменьшают значение переменной на 1.
ШАБЛОНЫ, КОТОРЫЕ ИСПОЛЬЗУЮТ НЕСКОЛЬКО ТИПОВ
Предыдущее определение шаблона для функции max использовало единственный общий тип Т. Очень часто в шаблоне функции требуется указать несколько типов. Например, следующие операторы создают шаблон для функции show_array, которая выводит элементы массива. Шаблон использует тип Т для определения типа массива и тип Т1 для указания типа параметра count:
templateclass T,class T1 void show_array(T *array,T1 count)
{
T1 index;
for (index =0; index count; index++) cout array[index] ' ';
cout endl;
}
Как и ранее, программа должна указать прототипы функций для требуемых типов:
void show_array(int *, int);
void show_array(float *, unsigned);
Следующая программа SHOW_TEM.CPP использует шаблон для создания функций, которые выводят массивы типа int и типа float.
#include iostream.h
templateclass T,class T1 void show_array( T *array,T1 count)
{
T1 index;
for (index =0; index count; index++) cout array[index] “ ' ';
cout endl;
}
void show_array(int *, int);
void show_array(float *, unsigned);
void main(void)
{
int pages[] = { 100, 200, 300, 400, 500 };
float pricesH = { 10.05, 20.10, 30.15 };
show_array(pages, 5);
show_array(prices, 3);
}
Шаблоны и несколько типов
По мере того как шаблоны функций становятся более сложными, они могут обеспечить поддержку нескольких типов. Например, ваша программа может создать шаблон для функции с именем array_sort, которая сортирует элементы массива. В данном случае функция может использовать два параметра: первый, соответствующий массиву, и второй, соответствующий количеству элементов массива. Если программа предполагает, что массив никогда не будет содержать более 32767 значений она может использовать тип int для параметра размера массива. Однако более универсальный шаблон мог бы предоставить программе возможность указать свой собственный тип этого параметра, как показано ниже:
templateclass Т, class T1 void array_sort(T array[], T1 elements)
{
// операторы
}
С помощью шаблона array_sort программа может создать функции которые сортируют маленькие массивы типа float (менее 128 элементов) и очень большие массивы типа int, используя следующие прототипы:
void array_sort(float, char);
void array_sort(int, long);
Символьные строки
Символьные строки хранят такую информацию, как имена файлов, названия книг, имена служащих и другие символьные сочетания. Большинство программ на C++ широко используют символьные строки. Далее вы узнаете, что в C++ символьные строки хранятся в массиве типа char, который заканчивается символом NULL (или ASCII 0). В данном уроке символьные строки рассматриваются более подробно. Вы узнаете, как хранить и обрабатывать символьные строки, а также как использовать функции библиотеки этапа выполнения, которые манипулируют символьными строками. К концу этого урока вы освоите следующие основные концепции:
Чтобы объявить символьную строку, вы должны объявить массив типа char,
Чтобы присвоить символы символьной строке, ваши программы просто присваивают символы элементам массива символьных строк.
Программы C++ используют символ NULL (ASCII 0), чтобы отметить последний символ строки.
C++ позволяет вашим программам инициализировать символьные строки при их объявлении.
Программы могут передавать символьные строки в функцию, как и любой массив.
Большинство библиотек этапа выполнения C++ обеспечивают набор функций, которые управляют символьными строками.
Программы на C++ хранят символьные строки как массив типа char. Большинство программ широко используют символьные строки. Экспериментируйте с каждой программой, представленной в этом уроке, чтобы освоиться с символьными строками. Вы обнаружите, что работа с символьными строками подобна работе с массивами, описанной в уроке 16.
Следите за ошибками несовпадения типов
Как уже обсуждалось, программа FIRSTCIN.CPP предполагает, что пользователь вводит значение в диапазоне от -32768 до 32767. Если вместо ввода числа вне этого диапазона, пользователь вводит буквы или другие символы, то возникает другая ошибка — ошибка несовпадения типов. Другими словами, программа ожидала значение одного типа (int), а пользователь ввел значение другого типа (char). Для примера запустите программу второй раз. Когда программа запросит число, введите буквы АВС. Как и раньше, возникнет ошибка, поскольку программа ожидает целое число, а не буквы.
Выполните подобные эксперименты с программой TWONBRS, вводя бессмысленные значения или числа с плавающей точкой. Вы обнаружите, что программа наталкивается на те же ошибки. В последующих уроках вы научитесь выполнять операции ввода таким образом, чтобы уменьшить возможность подобных ошибок. А сейчас просто помните, что такие ошибки могут возникать.