pa->b.p(); pa->pb->p();
bb.p(); pbb->p();
a.b.j =7; a.Show(); // Разнообразие присвоений
pa->b.j =8; pa->Show();
pa->pb->j=9; pa->Show();
a.pb->j =10; a.Show();
bb.j =11; bb.p();
}
Обратите внимание на то, как объявлены объекты вложенного класса B. Внутри класса A — это простое объявление, следующее сразу за объявлением класса B. Внутри main — это объявление вида A::B bb;. Полезно провести сравнение со случаем структур. Наличие объектов вложенного класса и указателей на них порождают множество вариантов доступа к методу вложенного класса. Так, оператор a.b.p(); обращается к методу void p() класса B, который вложен в класс A. При этом будет выведена строка j=2. Это же действие имеет другое словесное описание. Объекту b, являющемуся внутренним данным объекта a, послано сообщение в виде имени метода p().
Сам метод может быть идентифицирован как void A::B::p();, что кратко передает суть факта вложенности. Так как в классе A имеется указатель pb, который содержит адрес объекта b, то тот же самый метод можно вызвать с помощью указателя: a.pb->p();. Указатель pa содержит адрес вновь созданного объекта класса A. Вызов pa->b.p(); приведет к появлению на экране строки j=4. Два указателя используются при вызове pa->pb->p();. Здесь важно понимать, что указатель pa объявлен и действует внутри main, а указатель pb является public-данным класса A, и только благодаря этому факту доступен внутри main.
Объект bb объявлен внутри main как объект класса B, вложенного в класс A. Прямой вызов все того же метода p() для объекта bb выглядит так: bb.p();. На экране появится строка j=5. Косвенный вызов: pbb->p(); приведет к появлению строки j=6. Благодаря наличию указателей как на вложенный, так и на объемлющий классы, способы доступа к полю данных вложенного класса также могут быть весьма разнообразными. Однако, следует иметь ввиду, что прямой доступ возможен только потому, что поле j расположено в секции public вложенного класса. Если бы это было не так, то указанное поле можно было бы модифицировать внутри main только с помощью методов вложенного класса B. В примере таких методов у класса B не имеется.
Объясните, что будет выведено на экран монитора в последнем примере. Что произойдет, если два объявления int i; и class B перенести в секцию private класса A? Что будет, если объявление int j; перенести в секцию private класса B?
Массив объектов какого-либо класса, является обычной структурой данных в ООП. Он обладает следующей особенностью: класс, массив объектов которого необходимо определить, должен иметь конструктор без параметров (default constructor). Предположим, что мы хотим иметь массив объектов класса, но, в то же время, не хотим отказываться от возможности инициализировать поля данных объекта при его создании. В этом случае можно поступать так, как это сделано в следующем примере:
class Resistors
{
double r;
void Assign (double rs) { r=rs; } // Private-метод
public:
Resistors (double rs) { r=rs; } // Конструктор с параметром
Resistors () { Assign(100.); } // Конструктор без параметров
double Parallel (Resistors q)
{
return (q.r*r/(q.r+r));
}
double Series (Resistors q)
{
return (r+q.r);
}
void pr () { printf ("r=%6.2f",r); }
~Resistors() { puts("Destructor invoked"); }
};
void main ()
{
Resistors r1 (500), r2 (1000), r3;
printf("\n Class Resistors Demo\n");
printf("\n R1: "); r1.pr();
printf("\n R2: "); r2.pr();
printf("\n R3: "); r3.pr();
printf("\n R1 + R2 =%6.2f\n R1 || R2 =%6.2f", r1.Series(r2), r1.Parallel(r2));
Resistors many[6]; // Объявление массива объектов
puts("\n\n Array resistors\n");
for (int i=0; i<4; i++,putch('\n'))
many[i].pr();
}
При объявлении Resistors r1(500), r2(1000); будет вызван первый конструктор с параметром. При объявлении r3, а также массива объектов класса many[100]; будет вызван второй конструктор без параметров. Заботу о выяснении вида конструктора берет на себя компилятор C++. Определяющим фактором при этом является тип параметров. В нашем случае объявление массива объектов Resistors many[6]; заставляет компилятор выбрать второй конструктор. Формально это правило звучит так: объект класса с конструктором может быть членом агрегата (массива) тогда и только тогда, когда он либо не имеет конструктора (тогда компилятор предоставит ему конструктор по умолчанию), или если один из его конструкторов не имеет аргументов. В последнем случае именно этот конструктор будет использован при создании агрегата. Если элемент агрегата — объект класса с деструктором, то этот деструктор будет вызван для при уничтожении агрегата.
В нашем примере тело конструктора без параметров представляет собой вызов private-функции Assign. Отличие ее от public-метода состоит в том, что имя метода невидимо, недоступно за пределами класса. Так, например, в главной функции оператор вида: r1.Assign(2.); был бы незаконным и вызвал бы сообщение об ошибке на стадии компиляции. Однако, метод доступен внутри любого другого метода этого же класса и позволяет присвоить внутренним данным какое-то фиксированное значение. Обратите внимание на то, что сопротивление r для всех элементов массива many[6] (объектов класса Resistors) принимает значение 100., несмотря на то, что мы не пользовались оператором цикла для пробега по элементам и явного присвоения. На вопрос: сколько строк вида Destructor invoked будет выведено при запуске примера правильным будет ответ: 11 раз. Это объясняется тем, что в main объявлено 9 объектов и, кроме того, два объекта копируются (а затем уничтожаются) при входе в методы Series и Parallel.
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.