Второй пример
Если ваша программа передает указатели на параметры, параметры могут быгь любого типа, например int, float или char. Функция, которая использует указатели, объявляет переменные соответствующего типа, предваряя имя каждой переменной звездочкой, подтверждающей, что такая переменная является указателем. Следующая программа SWAPVALS.CPP передает адреса двух параметров типа float в функцию swap_values. Функция в свою очередь использует указатели на каждый параметр, чтобы обменять значения параметров:
#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;
swap_values(big, small);
cout "Big содержит " big endl;
cout "Small содержит " small endl;
}
Как видите, программа передает параметры в функцию swap_values по адресу. Внутри функции программа использует указатели на ячейки памяти параметров. Давайте более внимательно посмотрим на действия внутри функции swap_values. Как видите, функция объявляет а и b как указатели на значения типа float:
void swap_values(float *a, float *b)
Однако функция объявляет переменную temp просто как float, а не как указатель на float. float temp;
Рассмотрим следующий оператор:
temp = *а;
Этот оператор побуждает C++ присвоить переменной temp значение указываемое переменной а (т. е. значение переменной big, равное 10000.0). Поскольку temp имеет тип float, присваивание корректно. Переменная-указатель представляет собой переменную, которая хранит адрес. Следующий оператор объявляет temp как указатель на ячейку памяти, содержащую значение типа float.
float *temp;
В данном случае temp может хранить адрес значения с плавающей точкой но не само значение.
Если вы удалите оператор разыменования (*), стоящий перед переменной а внутри присваивания, то оператор будет пытаться присвоить значение, хранимое в а (которое является адресом), переменной temp. Поскольку temp может содержать значение с плавающей точкой, но не адрес значения с плавающей точкой, возникнет ошибка.
Не беспокойтесь, если вы не можете свободно обращаться с указателями, вы будете изучать их более подробно в части 3. На настоящий момент, однако, просто поймите, что, если хотите изменить в ваших функциях значения параметров, вы должны использовать указатели.
Использование ассемблерных листингов для лучшего понимания работы компилятора
Лучшим способом понять, как компилятор C++ трактует указатели, является исследование ассемблерного вывода компилятора. Большинство компиляторов C++ обеспечивают ключ командной строки, который вы можете использовать, чтобы указать компилятору выводить ассемблерный листинг. Читая ассемблерный листинг, вы можете лучше понять, как компилятор использует стек, когда передает параметры в функцию.
Вы только что узнали, что, используя указатель, ваша функция может сканировать строку символов, пока не будет обнаружен символ NULL. Следующая программа PTR_LEN.CPP использует указатель на строку в функции string_length для определения количества символов в строке:
#include iostream.h
int string_length(char *string)
{
int length = 0;
while (*string != '\0')
{
length++;
string++;
}
return(length);
}
void main(void)
{
char title[] = "Учимся программировать на языке C++";
cout title " содержит " string_length(title) " символов";
}
Как видите, функция string_length сканирует символы строки до тех пор, пока не встретит символ NULL.
Увеличение указателя на символьную строку
Когда программа передает массив в функцию, C++ передает адрес памяти первого элемента этого массива. Используя переменную-указатель, функция может перемещаться по содержимому массива, просто увеличивая значение указателя. Например, предположим, что программа передает в функцию символьную строку "Привет". Внутри функции переменная-указатель сначала указывает на участок памяти, который содержит букву 'П'. Когда функция увеличивает указатель, то он далее указывает на участок памяти, который содержит букву 'р'. По мере увеличения функцией значения указателя, он поочередно указывает на каждую букву в строке и наконец указывает на символ NULL.
При создании ваших собственных типов данных с помощью классов наиболее общей операцией будет проверка, являются ли два объекта одинаковыми. Используя перегрузку, ваши программы могут перегрузить операторы равенства (==), неравенства (!=) или другие операторы сравнения. Следующая программа COMP_STR.CPP добавляет новый оператор в класс string, который проверяет, равны ли два объекта string. Используя перегрузку операторов, ваши программы могут проверять, содержат ли строковые объекты одинаковые строки, как показано ниже:
if (some_string == another_string)
Ниже приведена реализация программы COMP_STR.CPP:
#include iostream.h
#include string.h
class string
{
public:
string(char *); // конструктор
char * operator +(char *);
char * operator -(char);
int operator ==(string);
void show_string(void);
private:
char data[256];
};
string::string(char *str)
{
strcpy(data, str);
}
char * string::operator +(char *str)
{
return(strcat(data, str));
}
char * string::operator -(char letter)
{
char temp[256];
int i, j;
for (i = 0, j = 0; data[i]; i++) if (data[i] 1= letter) temp[j++] = data[i];
temp[j] = NULL;
return(strcpy(data, temp));
}
int string::operator ==(string str)
{
int i;
for (i = 0; data[i] == str.data[i]; i++)
if ((data[i] == NULL) (str.data[i] == NULL)) return(1); // Равно
return (0); //He равно
}
void string::show_string(void)
{
cout data endl;
}
void main(void)
{
string title( "Учимся программировать на C++");
string lesson("Перегрузка операторов");
string str( "Учимся программировать на C++");
if (title == lesson) cout "title и lesson равны" endl;
if (str == lesson) cout "str и lesson равны" endl;
if (title == str) cout "title и str равны" endl;
}
Как видите, перегружая операторы подобным образом, вы упрощаете понимание ваших программ.
Следующая программа PEDIGREE.CPP создает класс dog, который содержит несколько полей данных и функцию show_breed. Программа определяет функцию класса вне определения самого класса. Затем программа создает два объекта типа dog и выводит информацию о каждой собаке:
#include iostream.h
#include string.h
class dogs
{
public:
char breed[64];
int average_weight;
int average_height;
void show_dog(void) ;
};
void dogs::show_breed(void)
{
cout "Порода: " breed endl;
cout "Средний вес: " average_weight endl;
cout "Средняя высота: " average_height endl;
}
void main(void)
{
dogs happy, matt;
strcpy(happy.breed, "Долматин") ;
happy.average_weight = 58;
happy.average_height = 24;
strcpy(matt.breed, "Колли");
matt.average_weight =22;
matt.average_height = 15;
happy.show_breed() ;
matt.show_breed();
}
Предположим, например, что вы используете следующий базовый класс book внутри существующей программы:
class book
{
public:
book (char *, char *, int);
void show_book(void);
private:
char title[64];
char author[б 4];
int pages;
};
Далее предположим, что программе требуется создать класс library_card, который будет добавлять следующие элементы данных в класс book:
char catalog[64];
int checked_out; // 1, если проверена, иначе О
Ваша программа может использовать наследование, чтобы породить класс library _card из класса book, как показано ниже:
class library_card : public book
{
public:
library_card(char *, char *, int, char *, int);
void show_card(void);
private:
char catalog[64] ;
int checked_out;
};
Следующая программа BOOKCARD.CPP порождает класс library_card из клacca book:
#include iostream.h
#include string.h
class book
{
public:
book(char *, char *, int);
void show_book(void);
private:
char title [64];
char author[64];
int pages;
};
book::book(char •title, char *author, int pages)
{
strcpy(book::title, title);
strcpy(book::author, author);
book::pages = pages;
}
void book::show_book(void)
{
cout "Название: " title endl;
cout "Автор: " author endl;
cout "Страниц: " pages endl;
}
class library_card : public book
{
public:
library_card(char *, char *, int, char *, int);
void show_card(void) ;
private:
char catalog[64];
int checked_out;
};
library_card::library_card(char *title, char *author, int pages, char *catalog, int checked_out) : book(title, author, pages)
Следующая программа ALLOCARR.CPP выделяет память для хранения массива из 1000 целочисленных значений. Затем она заносит в массив значения от 1 до 1000, выводя их на экран. Потом программа освобождает эту память и распределяет память для массива из 2000 значений с плавающей точкой, занося в массив значения от 1.0 до 2000.0:
#include iostreain.h
void main(void)
{
int *int_array = new int[1000];
float *float_array;
int i;
if (int_array 1= NULL)
{
for (i = 0; i 1000; i++) int_array[i] = i + 1;
for (i = 0; i 1000; i++) cout int_array[i] ' ';
delete int_array;
}
float_array = new float[2000];
if (float_array != NULL)
{
for (i = 0; i 2000; i++) float_array[i] = (i + 1) • 1.0;
for (i = 0; i 2000; i++) cout float_array[i] ' ' ;
delete float_array;
}
}
Как правило, ваши программы должны освобождать память с помощью оператора delete по мере того, как память становится программам не нужна.