Программирование >>  Полиморфизм без виртуальных функций в с++ 

1 ... 13 14 15 [ 16 ] 17 18 19 ... 144


Производные классы {

Для этого в языке нет прямой поддержки, но того же эффекта можно достигнуть с помощью аондортного препроцессора С. Например:

class ELEM stack {

ELEM * min, * top, * max;

void new(int), delete(void); public:

void push(ELEM);

ELEM pop(void);

Это объявление можно поместить в заголовочный фойл и с помощью мокросов расширять для кождого типо ELEM, который предпологоется использовать:

#define ELEM long

#define ELEM stack long stack

linclude stack.h

#undef ELEM

#undef ELEM stack

typedef class x X;

#define ELEM X

#define ELEM stack X stack

linclude stack.h

#undef ELEM

#undef ELEM stack

class long stack Is (1024); class long stack ls2(512); class X stack xs(512);

Конечно, этот способ далек от совершенства, зото прост .

Это был один из самых ранних и самых фубых методов. Для использования в реальных профаммах он не годился, так как был причиной слишком многих ошибок, поэтому вскоре я определил несколько стандартных макросов, склеивающих лексемы , и порекомендовал применять их для реализации обобщенных классов [Stroustrup, 1986, §7.3.5]. В конечном итоге отсюда взяли свое начало шаблоны С++ и техника их совместного использования с базовыми классами для выражения общих свойств инстанцированных шаблонов (см. раздел 15.5).

2.9.3. Модель размещения объекта в памяти

Реализация производного класса заключалась в объединении его членов и членов базового классов. Например, если есть объявления

class А {

int а; public:

/* функции-члены */



class В : public A {

int b; public:

/* функции-члены */

TO объект класса В будет представлен структурой

struct в { /* сгенерированный С-код */

int а; int b;

to есть

Конфликты между именами членов базового и производного классов устранялись самим компилятором, который присваивал им подходящие индивидуальные имена. Вызовы обрабатывались так, как если бы никакого наследования не было. По сравнению с С вовсе не возникало дополнительного расхода времени или памяти.

2.9.4. Ретроспектива

Было ли разумно избегать виртуальных функций в С with Classes? Да, язык и без них был полезен, а их отсутствие позволило на некоторое время отложить длительные дебаты о полезности, правильном использовании и эффективности. За это время были разработаны такие языковые механизмы и методы программирования, которые оказались кстати даже при наличии более мощных механизмов наследования. Эти механизмы и методы использовались как противовес стремлению некоторых программистов применять исключительно наследование, как будто больше в языке ничего не было (см. раздел 14.2.3). В частности, классы использовались для реализации таких конкретных типов, как complex и string, и интерфейс классов завоевал популярность. Класс stack, реализованный как интерфейс к более общему классу dequeue, - это пример наследования без виртуальных функций.

Нужны ли были виртуальные функции в С with Classes для решения тех задач, на которые он был нацелен? Да, поэтому они и были включены в первое из важнейших расширений при переходе к С++.

2.10. Модель защиты

Перед тем как приступить к созданию С with Classes, я работал с операционными системами. Происхождение механизмов защиты в С++ следует искать в концепции защиты в Кембриджском компьютере САР и аналогичных системах, а не в каких-то работах по языкам программирования. Единицу защиты составляет



Модель защиты

класс, и фундаментальное правило гласит, что нельзя предоставить доступ к классу самому себе. Только явные объявления, по.мещенные в объявление класса (предположительно его создателем), могут разрешить доступ. По умолчанию вся информация закрыта.

Для предоставления доступа к члену его следует поместить в открытой (public) части объявления класса либо объявить функцию или класс дружественными с помошью ключевого слова friend. Например:

class X {

/* представление */ public:

void f(); /* функция-член, имеющая доступ к представлению */

friend void g{); /* глобальная функция, имеющая доступ к */ /* представлению */

Первоначально дружественными могли быть только классы, то есть всем функциям-членам одного класса предоставлялся доступ ко всем членам другого класса, в объявлении которого первый класс имел спецификатор friend. Но позже было сочтено, что удобно предоставлять такой доступ и отдельным функциям, в частности, глобальным (см. также раздел 3.6.1).

Объявление дружественности виделось как механизм, аналогичный до.менам защиты (protection domain), предоставляющим право чтения-записи другим объектам. Это явная часть объявления класса. Поэтому я никогда не усматривал в повторяющихся утверждениях о том, что объявление friend, дескать, нарушает инкапсуляцию , ничего, кроме невежества и путаницы в терминологии.

Даже в первой версии С with Classes модель защиты применялась как к базовым класса.м, так и к членам. Это означает, что производный класс мог наследовать базовому открыто или закрыто. Введение открытого и закрытого наследования базовых классов примерно на пять лет опережает дебаты по поводу наследования интерфейса и реализации [Snyder, 1986], [Liskov, 1987]. Если вы хотите наследовать только реализацию, пользуйтесь в С++ закрытым наследованием. Открытое наследование дает пользователям производного класса доступ ко всему интерфейсу, предоставляемому базовым классом. При закрытом наследовании базовый класс можно считать деталью реализации, даже его открытые члены недоступны иначе как через интерфейс, предоставленный производным классом.

Для того чтобы иметь полупрозрачные области действия , был предложен механизм, разрешающий доступ к отдельным открытым членам из закрыто наследуемого базового класса [Stroustrup, 1982b]:

class vector {

/* ... */ public:

/* ... */

void print(void);



1 ... 13 14 15 [ 16 ] 17 18 19 ... 144

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика