Создание инициализированных и неинициализированных массивов
Если создаются как инициализированные, так и неинициализированные массивы объектов, то возникает особая ситуация. Рассмотрим следующий класс:
class C
{
int i;
public:
C(int j) { i=j; }
int get_i() { return i; }
};
Здесь конструктор требует один аргумент. В результате объявления любого массива элементов данного класса требуется, чтобы эти элементы были инициализированы. Следующее объявление массива является ошибочным:
C а[9]; // ошибка, конструктор требует инициализатор
Причина, по которой это объявление неверно, связана с тем, что подобное объявление, поскольку оно не содержит инициализации, требует наличия конструктора без параметров. Так как класс C не имеет конструктора без параметров, то компилятор выдает сообщение об ошибке. Для того чтобы решить эту проблему, необходимо перегрузить конструктор, добавив еще один — без параметров. В таком случае оба типа массивов — с инициализированными и неинициализированными элементами — становятся допустимыми. (Перегрузка конструкторов подробно обсуждается в следующей главе). В качестве примера ниже приведена усовершенствованная версия объявления класса C:
class C
{
int i;
public:
C() { i=0; } // вызывается для неинициализированных массивов
C(int j) { i=j; } // вызывается для инициализированных массивов
int get_i() { return i; }
};
Для объявленного таким образом класса оба следующих объявления являются допустимыми:
C a1[3] = {3, 5, 6}; // инициализированный
C а2[9]; // неинициализированный
В языке С можно получить доступ к структуре непосредственно или с использованием указателей на эту структуру. Аналогичным образом в C++ можно ссылаться на объект непосредственно, как это имело место во всех предыдущих примерах, или используя указатель на этот объект. Указатели на объекты являются одним из важнейших понятий C++.
Для доступа к членам объекта через сам объект используется оператор «точка» (.). Если же используется указатель на объект, тогда необходимо использовать оператор «стрелка» (->). Использование операторов «точка» и «стрелка» аналогично их использованию для структур и объединений.
Указатель на объект объявляется с использованием того же синтаксиса, что и указатели на данные других типов:
#include <iostream.h>
class P_example
{
int num;
public:
void set_num(int val) { num = val; }
void show_num();
};
void P_example::show_num()
{
cout << num << "\n";
}
int main()
{
P_example obj, *p; // объявление объекта и указателя на него
obj.set_num(1); // прямой доступ
р = &obj; // присваивание р адреса obj
p->show_num(); // доступ с помощью указателя
return 0;
}
Обратим внимание, что адрес объекта obj получен с использованием оператора взятия адреса & точно так же, как берется адрес переменной любого типа.
Инкремент или декремент указателя изменяет его таким образом, что он всегда указывает на следующий элемент базового типа. То же самое справедливо и для объектов.
#include <iostream.h>
class P_example
{
int num;
public:
void set_num(int val) { num = val; }
void show_num();
};
void P_example::show_num()
{
cout << num << "\n";
}
int main()
{
P_example obj[2], *p;
obj[0].set_num(10); // прямой доступ к объекту
obj[1].set_num(20);
p = &obj[0]; // получение указателя на первый элемент
p->show_num(); // вывод значения obj[0] с помощью указателя
р++; // переход к следующему объекту
p->show_num(); // вывод значения obj[1] с помощью указателя