Волков Владимир
Шрифт:
23. Теперь нужно попробовать откомпилировать проект. Но будет выведено сообщение об ошибке \'age\': cannot access private member declared in class \'Dog\'. Член класса с модификатором private не виден извне, и попытка получить к нему доступ в коде обработчика кнопки Assign обречена на неудачу.
24. Переместить поле age в область protected, как показано в листинге 5.12.
Листинг 5.12class Dog {
protected:
int age;
public:
Dog;
…25. Попытка откомпилировать проект закончится так же, как и предыдущая, поскольку снова происходит обращение к полю protected извне класса. Когда поле находилось в зоне public, такой ошибки не было.
26. Изменить метод Add класса CleverDog, как показано в листинге 5.13.
Листинг 5.13class CleverDog: public Dog {
void Add(int x, int y){
age= 200;
};27. Изменить обработчик щелчка на кнопке Assign, как показано в листинге 5.14. Листинг 5.14
case IDC_BUTTON3:
Dogs[0]->Add(0,0);
break;28. После внесения этих изменений проект нормально компилируется, и нажатие кнопки Assign не приводит к возникновению ошибки. Поскольку класс Dog является базовым классом для CleverDog, то метод Add дочернего класса получает доступ к полю age, объявленному в секции protected базового класса.
Перегрузка методов
Гибкость использования классов расширяется за счет использования перегрузки методов. Перегрузка методов позволяет объявлять в одном классе несколько методов с одним и тем же именем, но разным составом параметров. Этот принцип иллюстрируется в упражнении.
Упражнение 5.1 (продолжение)
29. Добавить в класс Dog еще один метод Speak, объявление которого приведено в листинге 5.15. Этот метод почти ничем не отличается от уже существовавшего метода Speak, кроме того, что он принимает в качестве параметра целое число и отображает его значение.
Листинг 5.15void Speak(int x){
char mm[32];
wchar_t *szStr = L"";
wchar_t mstr[32];
sprintf(mm,"Перегрузка, значение параметра: %d", x);
mbstowcs(mstr, mm, 32);
szStr = mstr;
MessageBox(NULL, szStr, TEXT(«TUT»), 0);
};30. Теперь нужно переписать обработчик щелчка на кнопке Assign, как показано в листинге 5.16. Листинг 5.16
case IDC_BUTTON3:
Dogs[0]->Speak;
Dogs[0]->Speak(350);
break;Компиляция и выполнение программы пройдут без ошибок. В C++ для обозначения перегруженных функций не нужны никакие особенные директивы. Компилятор, встретив функции с одинаковыми именами, но разным составом параметров, сам понимает, что они перегружены, и во время вызова функции именно по параметрам определяет, какую именно из перегруженных функций надо вызывать.
Приведение типов
В C++ есть два способа приведения типов, заимствованных из C. Можно использовать «тихое» приведение типов, когда разрядность приводимого типа меньше или равна разрядности типа, к которому осуществляется приведение. В этом случае потери информации не происходит, и приведение может быть осуществлено в момент присваивания, без применения особых операторов. Следующий фрагмент кода демонстрирует работу подобного приведения типа.byte x = 12; float y = x;
Также можно использовать явное приведение типов. Оно позволяет приводить один тип к другому при возможности потери или искажения информации. В этом случае ответственность за возможную потерю информации перекладывается на плечи программиста. Явное приведение типов выполняется при помощи оператора . Следующий фрагмент кода демонстрирует его использование.
(const unsigned short *)szStr
Но С++ не может ограничиваться этими двумя способами приведения типов. В С++ есть еще четыре оператора приведения типа. Прежде всего следует рассмотреть оператор const_cast. Если есть указатель, объявленный с модификатором const, и его нужно передать в качестве аргумента методу, в котором указатель принимается без этого модификатора, то нужно использовать оператор const_cast. Этот оператор удаляет модификатор const из объявления передаваемого указателя. Синтаксис применения этого оператора показан ниже.
const_cast<type>(exp)
В параметре type указывается тип, к которому приводится константа exp. Предположим, что есть некоторая функция, которая принимает в качестве аргумента указатель на int.
void my_func(int *x);
Помимо этого есть константа const int x, значение которой нужно передать в качестве аргумента в эту функцию. Вызов my_func(&x) закончится ошибкой компиляции, поскольку объявленный и передаваемый типы не соответствуют. А с преобразованием const_cast проблем не будет:
my_func(const_cast<int *>(&x));
Оператор dynamic_cast позволяет приводить типы во время выполнения программы. Его синтаксис показан ниже.
dynamic_cast<type*>(exp)
Этот оператор использует для своей работы RTTI (информация о типе во время выполнения программы). Основное назначение этого оператора – обеспечить приведение объектов базового типа к объектам производного типа. Если преобразование осуществлено, то оператор возвращает указатель на производный тип. Если преобразование нельзя провести, то оператор возвращает пустой указатель. Оператор reinterpret_cast позволяет преобразовывать указатели. Его синтаксис приведен ниже.
reinterpret_cast<type>(exp)
Этот оператор позволяет преобразовать указатель на один тип (exp) в указатель на другой тип <type>. Чаще всего его используют для преобразователя нетипизированного указателя void* в указатель на конкретный тип. Оператор static_cast применяется так же, как и явный оператор приведения типов.
Создание проектов MFC
После того как были рассмотрены основные вопросы объектно-ориентированного программирования в eVC, можно перейти к обсуждению создания проектов на основе MFC (Microsoft Foundation Classes).
MFC создавались как стройная иерархия классов, позволяющая в полной мере использовать такое преимущество объектно-ориентированного программирования, как повторное использование кода, инкапсулированного в объекты. Проанализировав основные паттерны программирования в Windows, программисты Microsoft выделили наиболее часто используемые объекты, модели поведения приложений и шаблоны кода, на основе которых они создали свою объектную иерархию. Эта иерархия дала возможность разработчикам сосредоточить свое внимание на реализации логики работы приложения, а не деталей его функционирования. С другой стороны, MFC производит стандартизацию основных операций и стиля программирования. Когда разработчик программирует в MFC, он получает в свое распоряжение большое количество шаблонов и мастеров, но взамен вынужден ограничить свою свободу дополнительными правилами, такими, например, как отказ от множественного наследования внутри иерархии классов MFC.