Бьерн Страуструп. Язык программирования С++ Второе дополненное издание



Pdf көрінісі
бет241/256
Дата11.07.2022
өлшемі2,87 Mb.
#37591
1   ...   237   238   239   240   241   242   243   244   ...   256
13.4 Узловые классы 
В действительности иерархия классов строится, исходя из совсем другой концепции производных 
классов, чем концепция интерфейс-реализация, которая использовалась для абстрактных типов. Класс 
рассматривается как фундамент строения. Но даже, если в основании находится абстрактный класс, он 
допускает некоторое представление в программе и сам предоставляет для производных классов какие-
то полезные функции. Примерами узловых классов могут служить классы rectangle ($$6.4.2) и satellite 
($$6.5.1). Обычно в иерархии класс представляет некоторое общее понятие, а производные классы 
представляют конкретные варианты этого понятия. Узловой класс является неотъемлемой частью 
иерархии классов. Он пользуется сервисом, представляемым базовыми классами, сам обеспечивает 
определенный сервис и предоставляет виртуальные функции и (или) защищенный интерфейс, чтобы 
позволить дальнейшую детализацию своих операций в производных классах. 
Типичный узловой класс не только предоставляет реализацию интерфейса, задаваемого его базовым 
классом (как это делает класс реализации по отношению к абстрактному типу), но и сам расширяет 
интерфейс, добавляя новые функции. Рассмотрим в качестве примера класс dialog_box, который 
представляет окно некоторого вида на экране. В этом окне появляются вопросы пользователю и в нем 
он задает свой ответ с помощью нажатия клавиши или "мыши": 
class dialog_box : public window { 
// 
... 
public: 
dialog_box(const 
char* 
...); 
// 
заканчивающийся нулем список 


Бьерн Страуструп.
Язык программирования С++ 
 
343 
// 
обозначений клавиш 
// 
... 
virtual 
int 
ask(); 
}; 
Здесь важную роль играет функция ask() и конструктор, с помощью которого программист указывает 
используемые клавиши и задает их числовые значения. Функция ask() изображает на экране окно и 
возвращает номер нажатой в ответ клавиши. Можно представить такой вариант использования: 
void user() 

for (;;) { 
// 
какие-то команды 
dialog_box 
cont("continue", 
"try 
again", 
"abort", 
(char*) 
0); 
switch 
(cont.ask()) 

case 
0: 
return; 
case 
1: 
break; 
case 
2: 
abort(); 



Обратим внимание на использование конструктора. Конструктор, как правило, нужен для узлового 
класса и часто это нетривиальный конструктор. Этим узловые классы отличаются от абстрактных 
классов, для которых редко нужны конструкторы. 
Пользователь класса dialog_box ( а не только создатель этого класса) рассчитывает на сервис, 
представляемый его базовыми классами. В рассматриваемом примере предполагается, что существует 
некоторое стандартное размещение нового окна на экране. Если пользователь захочет управлять 
размещением окна, базовый для dialog_box класс window (окно) должен предоставлять такую 
возможность, например: 
dialog_box cont("continue","try again","abort",(char*)0); 
cont.move(some_point); 
Здесь функция движения окна move() рассчитывает на определенные функции базовых классов. 
Сам класс dialog_box является хорошим кандидатом для построения производных классов. Например, 
вполне разумно иметь такое окно, в котором, кроме нажатия клавиши или ввода с мышью, можно 
задавать строку символов (скажем, имя файла). Такое окно dbox_w_str строится как производный класс 
от простого окна dialog_box: 
class dbox_w_str : public dialog_box { 
// 
... 
public: 
dbox_w_str 

const 
char* 
sl, 
// строка запроса пользователю 
const 
char* 
... 
// список обозначений клавиш 
); 
int 
ask(); 
virtual 
char* 
get_string(); 
//... 
}; 
Функция get_string() является той операцией, с помощью которой программист получает заданную 
пользователем строку. Функция ask() из класса dbox_w_str гарантирует, что строка введена правильно, 
а если пользователь не стал вводить строку, то тогда в программу возвращается соответствующее 
значение (0). 
void user2() 


Бьерн Страуструп.
Язык программирования С++ 
 
344 

// 
... 
dbox_w_str file_name("please enter file name", 
"done", 
(char*)0); 
file_name.ask(); 
char* p = file_name.get_string(); 
if (p) { 
// используем имя файла 

else 

// имя файла не задано 

// 

Подведем итог - узловой класс должен: 
[1] 
рассчитывать на свои базовые классы как для их реализации, так и для представления 
сервиса пользователям этих классов; 
[2] 
представлять более полный интерфейс (т.е. интерфейс с большим числом функций-членов) 
пользователям, чем базовые классы; 
[3] 
основывать в первую очередь (но не исключительно) свой общий интерфейс на виртуальных 
функциях; 
[4] 
зависеть от всех своих (прямых и косвенных) базовых классов; 
[5] 
иметь смысл только в контексте своих базовых классов; 
[6] 
служить базовым классом для построения производных классов; 
[7] 
воплощаться в объекте. 
Не все, но многие, узловые классы будут удовлетворять условиям 1, 2, 6 и 7. Класс, который не 
удовлетворяет условию 6, походит на конкретный тип и может быть назван конкретным узловым 
классом. Класс, который не удовлетворяет условию 7, походит на абстрактный тип и может быть назван 
абстрактным узловым классом. У многих узловых классов есть защищенные члены, чтобы предоставить 
для производных классов менее ограниченный интерфейс. 
Укажем на следствие условия 4: для трансляции своей программы пользователь узлового класса должен 
включить описания всех его прямых и косвенных базовых классов, а также описания всех тех классов, от 
которых, в свою очередь, зависят базовые классы. В этом узловой класс опять представляет контраст с 
абстрактным типом. Пользователь абстрактного типа не зависит от всех классов, использующихся для 
реализации типа и для трансляции своей программы не должен включать их описания. 


Достарыңызбен бөлісу:
1   ...   237   238   239   240   241   242   243   244   ...   256




©emirsaba.org 2024
әкімшілігінің қараңыз

    Басты бет