struct Value {
int nVal;
char *str;
};
А использовалось следующим образом:
void init(Value* val) {
val.nVal=10;
val.str=(char*)malloc(50);
}
То же самое, конечно, можно написать и на C++, но он предоставляет гораздо более мощное средство под названием класс. Этот пример можно переписать так (забегая немного вперед, скажу, что на практике чаще всего используются классы, подобные этому):
class CValue {
int nVal;
char* str;
public:
CValue() { nVal=0; str=NULL; }
~CValue() { delete[] str; }
CValue& Val(int val) { nVal=val; return *this; }
CValue& Str(const char* string) {
delete[] str;
str=new char[strlen(string)+1];
strcpy(str, string);
return *this;
}
int Val() const { return nVal; }
char* Str() const { return str; }
};
Вы спросите, что нам дает такое, казалось бы, громоздкое повторение предыдущей маленькой структуры. В первую очередь, контроль за значениями, хранящимися в переменных. Мы можем, например, ограничить диапазон переменной nVal значениями от 3 до 11, включив соответствующую проверку в функцию CValue& Val(int val). Благодаря спецификаторам доступа public и private (используемый неявно в начале класса) исключается несанкционированный доступ к переменным класса. Но не менее важно и то, что их значения не примут случайное значение (благодаря конструктору) и не произойдет утечки памяти (благодаря деструктору). Кроме этого очень сильно упрощается использование такой структуры.
CValue value;
// так происходит инициализация
value.Val(10).Str("string");
// так значения используются
int n=value.Val();
char *str=value.Str();
Каскадный вызов функций при инициализации возможен благодаря тому, что они возвращают ссылку на свой экземпляр класса. Это, в случае частого использования, может приводить к значительному ускорению работы программы и к получению более оптимального кода, генерируемого компилятором.
Теперь воспользуемся таким средством C++ как перегрузка операторов и добавим в наш класс следующие функции:
class CValue {
...
public:
CValue& operator=(int val) { return Val(val); }
CValue& operator=(const char* string) { return Str(string); }
operator int() const { return nVal; }
operator char*() const { return str; }
};
Это дало нам еще один способ использования объектов этого типа:
CValue value;
// инициализация
value=20;
// происходит вызов перегруженной
// функции CValue::operator=(int val)
value="string";
// происходит вызов перегруженной
// функции CValue::operator=(char* string)
// вот так теперь можно получить значения
// соответствующих переменных класса
int n=value; // неявное преобразование value к типу int
// n будет равно value.nVal
char *str=value; // неявное преобразование value к типу char*
// str будет равно value.str
Неправда ли это становится очень похоже на Бейсик? В конце приведу еще один пример типа, который можно использовать способом, аналогичным при использовании переменных в VB. Как известно, VB "равнодушен" к типу переменных – переменной можно присвоить и 5 и "25". В одном случае произойдет неявное преобразование из строки в число, в другом наоборот. То есть, я хочу сказать, что VB является языком со слабым контролем типов в отличие от C++, обладающим строгим контролем типов. Если кто-то скажет, что это – недостаток, то я его адресую к [1]. Примером, как можно "обойти" этот "недостаток", может служить этот шаблонный класс:
template class CVBValue {
T m_val;
public:
CVBValue() {};
CVBValue(T val) { m_val=val; }
T Val() const { return m_val; }
CVBValue& operator=(T val) { m_val=val; return *this; }
CVBValue& operator=(char* str) {
// тут происходит преобразование из char* в тип T
// если, конечно, известно как это сделать
return *this;
}
operator T() const { return m_val; }
};
Использование этого класса происходит уже известным вам способом:
CVBValue val; // создание экземпляра класса
val=2.5;
val="1.2345"; // преобразование из строки в тип double double
d=val; // получение текущего значения
Дальнейшее расширение класса зависит только от вашего воображения. Хочется вас предостеречь от излишнего упрощения использования типов. Программисты на VB могут ужаснуться, когда узнают, сколько может быть скрыто строчек кода за невинным, на первый взгляд, присваиванием. Но вы теперь это прекрасно осознаете и понимаете, что, чем более сложный код, вы напишете, тем больше вероятность появления ошибок. В данном случае, я имею в виду ошибки, появление которых можно обнаружить только во время исполнения программы. Что произойдет в описанном выше примере, если написать val="string"? В лучшем случае ничего, но вообще-то может возникнуть исключение (возможно в случае нехватки памяти). Это вынуждает нас помещать обычное приравнивание в блок
Читать дальше