Рейтинговые книги
Читем онлайн Программирование на Visual C++. Архив рассылки - Алекс Jenter

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 63 64 65 66 67 68 69 70 71 ... 156

В тех языках программирования, синтаксис которых находится под контролем создателей (таких как "Visual Basic" или "Delphi") концепция свойств реализована на уровне синтаксиса. В частности, обращение к свойствам объекта производится оператором присваивания, как при обращении к переменной-члену класса в C++. Однако, не стоит обольщаться по поводу простоты синтаксиса. Не стоит забывать, что в простейшем выражении типа

theObject.theProperty = theValue

производится неявный вызов функции.

К сожалению, строгий и стандартизированный язык C++ не позволяет добавлять в его синтаксис столь революционные новации. Какие же возможности предоставляет разработчику C++?

Наиболее простой и самый распространённый способ обеспечения инкапсуляции в C++ заключается в написании пары функций типа get_Value() и put_Value()  для каждого параметра. Заметим, что именно так реализованы свойства в технологии Automation. При использовании этого способа можно написать примерно такой класс:

class CValue {

private:

 int m_value;

public:

 int get_Value() {

  return m_value; // Или более сложная логика

 }

 void put_Value(int value) {

  m_value = value; // Или более сложная логика

 }

};

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

Хорошо это или плохо, но современные средства разработки "приучили" многих к использованию свойств в операторах присваивания и вообще обращению с ними, как с переменными-членами. Учитывая это, разработчики Microsoft Visual C++ добавили в синтаксис языка несколько "Microsoft Specific" конструкций. В частности, модификатор __declspec получил дополнительный параметр "property". Теперь в классе можно объявить "виртуальную" переменную и связать её с соответствующими функциями. Теперь класс может выглядеть примерно так:

class CValue {

private:

 int  m_value;

public:

 __declspec(property(get=get_Value, put=put_Value)) int Value;

 int get_Value() {

  return m_value; // Или более сложная логика

 }

 void put_Value(int value){

  m_value = value;     // Или более сложная логика

 }

};

Строчка сразу за "public:" объявляет "виртуальную" переменную типа int, при обращении к которой фактически будут вызваться функции. С этим классом можно будет работать примерно так:

CValue val; val.Value = 50; // На самом деле вызов put_Value()

int z = val.Value; // На самом деле вызов get_Value()

Чем не "настоящие" свойства? Однако следует заметить, что модификатор __declspec(property) был введён не для повседневного использования, а для встроенной в компилятор поддержки технологии COM. Дело в том, что директива импорта библиотеки типа (что бы знать, что это такое, читайте книжки по COM) #import заставляет компилятор VC автоматически генерировать вспомогательные классы-обёртки для объектов COM. Вот в этих "автоматических" классах модификатор __declspec(property) используется достаточно широко для максимальной приближенности к синтаксису Visual Basic'а. Приближенность к синтаксису VB достигает такой степени, что свойства сделаны индексными. Для этого, достаточно поставить квадратные скобки после объявления "виртуальной переменной":

__declspec(property(get=get_Value, put=put_Value)) int Value[]; 

После такого объявления свойство "Value" может принимать один или несколько параметров-индексов, передаваемых в квадратных скобках. Так, например, вызов

Val.Value[f]["two"] = 10;

Будет преобразован в вызов функции

Val.put_Value(f, "two", 10);

Главным недостатком описанного выше способа использования свойств в C++ является его зависимость от компилятора, пресловутая "Microsoft Specific". Впрочем, другой, не менее известный компилятор "Borland C++ Builder" реализует концепцию свойств далёким от стандарта способом. В любом случае часто требуется (или хочется) достичь независимости от компилятора и соответствия кода программы стандарту C++. Что же делать? Оказывается язык C++ позволяет реализовать концепцию свойств в стиле Visual Basic'а. Для этого необходимо воспользоваться шаблонами и перекрыть операторы присваивания и приведения типа. Но для начала необходимо провести некоторую подготовительную работу:

// Базовый класс шаблона свойства.

template <typename proptype, typename propowner> class property {

protected:

 typedef proptype (propowner::*getter)();

 typedef void (propowner::*setter)(proptype);

 propowner *m_owner;

 getter m_getter;

 setter m_setter;

public:

 // Оператор приведения типа. Реализует свойство для чтения.

 operator proptype() {

  // Здесь может быть проверка "m_owner" и "m_getter" на NULL

  return (m_owner->*m_getter)();

 }

 // Оператор присваивания. Реализует свойство для записи.

 void operator =(proptype data) {

  // Здесь может быть проверка "m_owner" и "m_setter" на NULL

  (m_owner->*m_setter)(data);

 }

 // Конструктор по умолчанию.

 property() : m_owner(NULL), m_getter(NULL), m_setter(NULL) {}

 //Конструктор инициализации.

 property(propowner * const owner, getter getmethod, setter setmethod) :

  m_owner(owner), m_getter(getmethod), m_setter(setmethod) {}

 // Инициализация

 void init(propowner * const owner, getter getmethod, setter setmethod) {

  m_owner = owner;

  m_getter = getmethod;

  m_setter = setmethod;

 }

};

Теперь класс, реализующий свойство можно написать так:

class CValue {

 private:

 int m_value;

 int get_Value() {

  return m_value; // Или более сложная логика

 }

 void put_Value(int value) {

  m_value = value; // Или более сложная логика

 }

public:

 property <int, CValue> Value;

 CValue() {

  Value.init(this, get_Value, put_Value);

 }

};

А вот код, использующий этот класс:

CValue val;

/*

Здесь вызывается оператор присваивания переменной-члена val.Value, и, следовательно, функция val.put_Value()

*/

val.Value = 50;

/*

Здесь вызывается оператор приведения типа переменной-члена val.Value, и, следовательно, функция val.get_Value()

*/

int z = va.Value;

Как можно видеть, получились "настоящие" свойства средствами только стандартного синтаксиса C++. Однако, описанный метод не лишен недостатков:

• При каждом обращении к "свойству" происходит два вызова функции.

• Использование таких "свойств" требует дополнительных затрат памяти из-за того, что на каждое "свойство" требуется 3 дополнительных указателя, что составляет 12 байт накладных расходов.

• Использование шаблонов приводит к увеличению размеров исполняемого кода, поскольку компилятор будет генерировать отдельный класс для каждой пары "proptype" и "propowner".

• Для каждого "свойства" необходимо не забыть произвести инициализацию в конструкторе класса-владельца. 

ВОПРОС-ОТВЕТ  Как научить программу реагировать на изменение содержимого буфера обмена? Автор: Александр Шаргин Версия текста: 1.0 Программа-пример CbView Программа-пример MfcCbView

В Windows существует понятие наблюдателя за буфером обмена (clipboard viewer), которым может стать любое окно. Наблюдатель получает от системы уведомления об изменении содержимого буфера обмена в виде сообщения WM_DRAWCLIPBOARD. Соответственно, в ответ на это сообщение программа может загрузить содержимое буфера обмена и выполнить с ним нужные операции (типичный пример – отобразить содержимое буфера обмена в окне).

Интересен способ взаимодействия системы с несколькими наблюдателями за буфером обмена. Дело в том, что с точки зрения Windows наблюдатель всегда один (он называется текущим), и только ему посылаются уведомления. Передача этих уведомлений дальше по цепочке наблюдаетей – задача приложения. Для этого каждая программа, регистрирующая наблюдателя за буфером обмена, получает и сохраняет в переменной HWND предыдущего наблюдателя, а затем передаёт ему сообщения с помощью одной из функций SendMessage, PostMessage и т.п. "Недобросовестная" программа, которая не передаёт уведомления дальше по цепочке, может нарушить работу других приложений и даже других экземпляров самой себя, поэтому писать такие программы настоятельно не рекомендуется.

Рассмотрим процесс работы программы-наблюдателя более подробно. Первое, что ей необходимо сделать – это зарегистрировать своё окно при помощи функции SetClipboardViewer, которая возвращает хэндл текущего наблюдателя и делает текущим наше окно. Как уже говорилось, переданный нам хэндл окна следует сохранить в переменной для дальнейшего использования. Например:

BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) {

 …

 static HWND hNextViewer;

 …

1 ... 63 64 65 66 67 68 69 70 71 ... 156
На этой странице вы можете бесплатно читать книгу Программирование на Visual C++. Архив рассылки - Алекс Jenter бесплатно.
Похожие на Программирование на Visual C++. Архив рассылки - Алекс Jenter книги

Оставить комментарий