Объектно-ориентированное программирование, страница 2

class Man  // Класс объектов

{

private:       // Секция закрытого доступа

char* name;   // Скрытые внутренние данные

int age;

public:        // Секция открытого доступа

void In ()    // Метод класса

{              

               // Здесь те же коды, что и ранее

}

void Out () { printf ("\n %s,\t Age:%d",Name,Age); } // Метод класса

};

void main() //==== Демонстрация использования класса

{

Man m;      // Объект m класса Man

m.In();   m.Out();  // Ввод, вывод данных

m.Age = -2000;      // Этот оператор ошибочен и ошибка выявляется на стадии компиляции

}

Описатели (спецификаторы) секций private и public определяют тип доступа к данным и методам. Переменные name, и age, снабженные описателем private, доступны только внутри методов класса. Теперь уже исключена возможность присвоения age=-2000; в функции main, так как age — это внутреннее private-данное любого объекта класса Man и доступ к нему возможен только с помощью методов, заданных в модуле описания класса. Попытка присвоить -2000 вызовет сообщение об ошибке на стадии компиляции. Теперь стало невозможным изменение значения внутреннего (private) поля данных незаконным способом, то есть минуя законные методы, инкапсулированные в классе. Законным способом считается доступ к данным с помощью методов класса. Важно то, что ошибка обнаруживается на этапе компиляции, и то, что правила игры автоматически соблюдаются для любого объекта класса.

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

В нашем примере путем инкапсуляции абстрактного понятия Человек в класс Man программист сам себе запрещает в будущем случайно изменять ключевые поля объектов (представителей) класса Man прямым присвоением извне. Инкапсуляция подразумевает заключение в жесткие рамки правил игры и готовность их соблюдать. В приведенном примере описание класса (definition) и реализация его методов (implementation) расположены в одном модуле, так как задача проста. При разработке реальных приложений модули описаний классов (definition modules) размещают в h-файлах. Они компилируется отдельно и именно в них задаются все правила игры с объектами классов.

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

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

class Class_Name

{

private:

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

// Они доступны только в методах класса Class_Name, а с их помощью и извне.

protected:

// Здесь расположены данные и методы, недоступные прямо.

// Они доступны в методах класса и в методах производных классов. Последние будут рассмотрены ниже.

public:

// Здесь расположены данные и методы, доступные обычным, прямым способом из внешних процедур.

};

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

При отсутствии описателей private, protected и public члены класса по умолчанию имеют тип private. Для структур и union-структур все члены по умолчанию, наоборот, имеют тип public.

Язык С++ временно допускает неполную декларация класса (incomplete declaration). Например,

class A;           // Неполная декларация класса А

class B { A *pa; };  // Указатель на класс A - член класса B

class A { B *pb; };  // Класс A декларирован полностью

//===== Возможные объявления переменных класса A

A obj;           // Объект класса A

A *p;            // Указатель на объекты класса A

A &ref = obj;    // Ссылка на класс A

A array[7];      // Массив объектов класса A

A Func();        // Функция, возвращающая объект класса A

Декларировать можно: объекты, указатели и ссылки на класс, массивы объектов класса, функции, возвращающие объект класса. Напомним: чтобы послать объекту сообщение, имея только его адрес (p), используется операция выбора p->Method(). Таким образом, эквивалентны три способа инициации метода класса:

class A { public:  void Method() { printf ("\nOk"); } };

void main()

{

A *p, obj;          // Указатель и объект класса A

p = &obj;           // p указывает на obj

   //-------- Три способа вызова функции ---------//

obj. Method();      // Сообщение объекту

(&obj)->Method();   // То же, но с помощью адреса

p->Method();        // То же, но с помощью указателя