Программирование >>  Решение нетривиальных задач 

1 ... 59 60 61 [ 62 ] 63 64 65 ... 77


f() {

static int have been called = 0;

if( !have been called )

have been called = 1; do one time initializations();

134. Избегайте инициализации в два приема

135. Суперобложки на Си++ для существующих интерфейсов редко хорошо работают

Как правило, переменная должна инициализироваться во время объявления. Разделение инициализации и объявления иногда обусловливается плохим проектированием в программе, которая написана не вами, как в следующем фрагменте, написанном для выполнения совместно с библиотекой MFC Microsoft:

f( CWnd *win ) CWnd - это окно

Следующая строка загружает буфер с шапкой окна (текстом в строке заголовка)

char buf[80]; /* = */

win->GetWindowText(buf, sizeof(buf));

...

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

Здесь имеется несколько проблем, первая из которых заключается в плохом проектировании класса CWnd (представляющем окно). Так как у окна есть текстовый атрибут, хранящий заголовок, то вы должны иметь возможность доступа к этому атрибуту подобным образом:

CString caption = win->caption();

и вы должны иметь возможность модифицировать этот атрибут так:

win->caption() = новое содержание ;

но вы не можете сделать этого в текущей реализации. Главная проблема состоит в том, библиотека MFC не была спроектирована в объектно-ориентированном духе - т.е. начать с объектов, затем выбрать, какие сообщения передавать между ними и какими атрибутами их наделить.



Вместо этого проектировщики Microsoft начали от существующего процедурного интерфейса (AP1 Си - интерфейса прикладного программирования для Windows на Си) и добавили к нему суперобложку на Си++, тем самым увековечив все проблемы существующего интерфейса. Так как в AP1 Си была функция с именем GetWindowText(), то проектировщики беззаботно сымитировали такой вызов при помощи функции-члена в своей оболочке CWnd. Они поставили заплату на интерфейс при помощи следующего вызова: CString str;

win->GetWindowText( str );

но это - не решение по двум причинам: по-прежнему требуется инициализация в два приема, и аргумент является ссылкой на результат.

Главный урок состоит в том, что проекты, основанные на процедурном подходе, радикально отличаются от объектно-ориентированных проектов. Обычно невозможно использовать код из одного проекта в другом без большой переработки. Простая оболочка из классов Си++ вокруг процедурного проекта не сделает его объектно-ориентированным.

Поучительно, я думаю, пошарить вокруг в поисках решения текущей проблемы с помощью Си++, но предупреждаю вас - здесь нет хорошего решения (кроме перепроектирования библиотеки классов). Моя первая попытка сделать оболочку вокруг CWnd показана на листинге 11.

Для обеспечения возможности win->text() = Новый заголовок необходим вспомогательный класс (window::caption). Вызов text() возвращает объект заголовка, которому затем передается сообщение присваиванием.

Главная проблема на листинге 11 заключается в том, что библиотека MFC имеет много классов, унаследованных от CWnd, и интерфейс, реализованный в классе window, не будет отражен в других потомках CWnd. Си++ является компилируемым языком, поэтому нет возможности вставлять класс в середину иерархии классов без изменения исходного кода.

Листинг 12 определяет другое решение для смеси Си++ с MFC. Я выделил класс window::caption в отдельный класс, который присоединяется к окну, когда оно инициализируется. Используется подобным образом:

f(CWnd *win)

caption cap( win )

CString s = cap; поддерживается преобразование в CString. cap = Новый заголовок ; использует операцию



1 class

3 public:

4 class caption

6 CWnd *target window; 7

8 private: friend class window;

9 caption( CWnd *p ) : target window(p) {}

11 public:

12 operator CString ( void ) const;

13 const caption & operator=( const CString &s );

14 }; 15

16 caption text( void );

17 };

18 --------------------------------------------------------19 caption window::text( void )

20 {

21 return caption( this );

22 }

23 --------------------------------------------------------24 window::caption:: operator CString( void ) const

25 {

26 CString output;

27 target window->GetWindowText( output );

28 return output; возвращает копию

29 }

30 --------------------------------------------------------31 const caption &window::caption:: operator=( const CString &s)

32 {

33 target window->SetWindowText( s );

34 return *this;

35 }

operator=(CString&)

Мне не нравится то, что изменение заголовка caption меняет также окно, к которому этот заголовок присоединен в этом последнем примере. Скрытая связь между двумя объектами может сама по себе быть источником недоразумений, будучи слишком похожей на побочный эффект макроса. Как бы то ни было, листинг 12 решает проблему инициализации.

Листинг 11. Обертка для CWnd: первая попытка window : public CWnd



1 ... 59 60 61 [ 62 ] 63 64 65 ... 77

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