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 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) :
Читать дальше