Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Виртуальный конструктор-копировщикСодержание книги
Поиск на нашем сайте
Конструкторы не могут быть виртуальными, из чего можно сделать вывод, что не может быть также виртуального конструктора-копировщика. Но иногда требуется, чтобы программа могла передать указатель на объект базового класса и правильно скопировать его в объект производного класса. Чтобы добиться этого, необходимо в базовом классе создать виртуальный метод Clone(). Метод Clone() должен создавать и возвращать копию объекта текущего класса. Поскольку в производных классах метод Clone() замещается, при вызове его создаются копии объектов, соответствующие выбранному классу. Программа, использующая этот метод, показана в листинге 11.11. Листинг 11.11. Виртуальный конструктор-копировщик 1: //Листинг 11.11. Виртуальный конструктор-копировщик 2: 3: #include <iostream.h> 4: 5: class Mammal 6: { 7: public: 8: Mammal():itsAge(1) { cout << "Mammal constructor...\n"; } 9: virtual ^Mammal() { cout << "Mammal destructor...\n"; } 10: Mammal (const Mammal & rhs); 11: virtual void Speak() const { cout << "Mammal speak!\n"; } 12: virtual Mammal* Clone() { return new Mammal(*this); } 13: int GetAge()const { return itsAge; } 14: protected: 15: int itsAge; 16: }; 17: 18: Mammal::Mammal (const Mammal & rhs):itsAge(rhs.GetAge()) 19: { 20: cout << "Mammal Copy Constructor...\n"; 21: } 22: 23: class Dog: public Mammal 24: { 25: public: 26: Dog() { cout << "Dog constructor...\n"; } 27: virtual ~Dog() { cout << "Dog destructor...\n"; } 28: Dog (const Dog & rhs); 29: void Speak()const { cout << "Woof!\n"; } 30: virtual Mammal* Clone() { return new Dog(*this); } 31: }; 32: 33: Dog::Dog(const Dog & rhs): 34: Mammal(rhs) 35: { 36: cout << "Dog copy constructor...\n"; 37: } 38: 39: class Cat: public Mammal 40: { 41: public: 42: Cat() { cout << "Cat constructor,,,\n"; } 43: ~Cat() { cout << "Cat destructor...\n"; } 44: Cat (const Cat &); 45: void Speak()const { cout << "Meow!\n"; } 46: virtual Mammal* Clone() { return new Cat(*this); } 47: }; 48: 49: Cat::Cat(const Cat & rhs): 50: Mammal(rhs) 51: { 52: cout << "Cat copy constructor..,\n"; 53: } 54: 55: enum ANIMALS { MAMMAL, D0G, CAT }; 56: const int NumAnimalTypes = 3; 57: int main() 58: { 59: Mammal *theArray[NumAnimalTypes]; 60: Mammal* ptr; 61: int choice, i; 62: for (i = 0; i<NumAnimalTypes; i++) 63: { 64: cout << "(1)dog (2)cat (3)Mammal: "; 65: cin >> choice; 66: switch (choice) 67: { 68: case DOG: ptr = new Dog; 69: break; 70: case CAT: ptr = new Cat; 71: break; 72: default: ptr = new Mammal; 73: break; 74: } 75: theArray[i] = ptr; 76: } 77: Mammal *OtherArray[NumAnimalTypes]; 78: for (i=0;i<NumAnimalTypes;i++) 79: { 80: theArray[i]->Speak(); 81: OtherArray[i] = theArray[i]->Clone(); 82: } 83: for (i=0;i<NumAnimalTypes;i++) 84: OtherArray[i]->Speak(); 85: return 0; 86: }
Результат: 1: (1)dog (2)cat (3)Mammal: 1 2: Mammal constructor... 3: Dog constructor... 4: (1)dog (2)cat (3)Mammal: 2 5: Mammal constructor... 6: Cat constructor... 7: (1)dog (2)cat (3)Mammal: 3 8: Mammal constructor... 9: Woof! 10: Mammal Copy Constructor... 11: Dog copy constructor... 12: Meow! 13: Mammal Copy Constructor... 14: Cat copy constructor... 15: Mammal speak! 16: Mammal Copy Constructor... 17: Woof! 18: Meow! 19: Mammal speak!
Анализ: Листинг 11.11 похож на два предыдущих листинга, однако в данной программе в классе Mammal добавлен один новый виртуальный метод — Clone(). Этот метод возвращает указатель на новый объект класса Mammal, используя конструктор-копировщик, параметр которого представлен указателем <<this. Метод Clone() замещается в обоих производных классах — Dog и Cat — соответствующими версиями, после чего копии данных передаются на конструкторы- копировщики производных классов. Поскольку Clone() является виртуальной функцией, то в результате будут созданы виртуальные конструкторы-копировщики, как показано в строке 81. Пользователю предлагается выбрать объект класса 0og, Cat или Mammal. Объект выбранного типа создается в строках 62-74. В строке 75 указатель на новый объект добавляется в массив данных. Затем осуществляется цикл, в котором для каждого объекта массива вызываются методы Speak() и Clone() (см. строки 80 и 81). В результате выполнения функции возвращается указатель на копию объекта, которая сохраняется в строке 81 во втором массиве. В строке 1 вывода на экран показан выбор пользователем опции 1 — создание объекта класса Dog. В создание этого объекта вовлекаются конструкторы базового и производного классов. Эта операция повторяется для объектов классов Cat и Mammal в строках вывода 4-8. В строке 9 вывода показано выполнение метода Speak() для объекта класса Dog. Поскольку функция Speak() также объявлена как виртуальная, то при обращении к ней вызывается та ее версия, которая соответствует типу объекта. Затем следует обращение еще к одной виртуальной функции Clone(), виртуальность которой проявляется в том, что при вызове из объекта класса Dog запускаются конструктор класса Mammal и конструктор-копировщик класса Dog. То же самое повторяется для объекта класса Cat (строки вывода с 12—14) и объекта класса Mammal (строки вывода 15 и 16). В результате создается массив объектов, для каждого из которых вызывается своя версия функции Speak().
Цена виртуальности методов
Поскольку объекты с виртуальными методами должны поддерживать v-таблицу, то использование виртуальных функций всегда ведет к некоторому повышению затрат памяти и снижению быстродействия программы. Если вы работаете с небольшим классом, который не собираетесь делать базовым для других классов, то в этом случае нет никакого смысла использовать виртуальные методы. Объявляя виртуальный метод в программе, заплатить придется не только за v- таблицу (хотя добавление последующих записей потребует не так уж много места), но и за создание виртуального деструктора. Поэтому следует подумать, имеет ли смысл преобразовывать методы программы в виртуальные, а если да, то какие именно.
Рекомендуется: Используйте виртуальные методы только в том случае, если программа содержит базовый и производные классы. Используйте виртуальный деструктор, если в программе были созданы виртуальные методы. Не рекомендуется: Не пытайтесь создать виртуальный конструктор.
Резюме
Сегодня вы узнали, как наследовать новые классы от базового класса. В этой главе рассматривалось наследование с ключевым словом public и использование виртуальных функций. Во время наследования в производные классы передаются все открыты e и защищенные данные и функции из базового класса. Защищенные данные базового класса открыты для всех производных классов, но закрыты для всех других классов программы. Но даже производные классы не могут получить доступ к закрытым данным и функциям базового класса. Конструкторы могут инициализироваться до выполнения тела конструктора. При этом вызывается конструктор базового класса, и туда могут быть переданы данные в виде параметров. Функции, объявленные в базовом классе, могут быть замещены в производных классах. Если при этом функция объявлена как виртуальная, а обращение к функции от объекта осуществляется с помощью указателя на объект или ссылки, то вызываться будет тот замещенный вариант функции, который соответствует типу текущего объекта. Методы базового класса можно вызывать явным обращением, когда в строке вызова сначала указывается имя базового класса с двумя символами двоеточия после него. Например, если класс Dog произведен от класса Mammal, то к методу базового класса напрямую можно обратиться следующим выражением: Mammal::walk(). Если в классе используются виртуальные методы, то следует объявить также и виртуальный деструктор. Он необходим для того, чтобы быть уверенным в удалении части объекта, относящейся к производному классу, если удаление объекта осуществлялось с помощью указателя базового класса. Нельзя создать виртуальный конструктор. В то же время можно создать виртуальный конструктор-копировщик и эффективно его использовать с помощью виртуальной функции, вызывающей конструктор-копировщик.
Вопросы и ответы
Наследуются ли данные и функции-члены базового класса в последующие поколения производных классов? Скажем, если класс Dog произведен от класса Mammal, а класс Mammal произведен от класса Animals, унаследует ли класс Dog данные и функции класса Animals? Да. Если последовательно производить ряд классов, последний класс в этом ряду унаследует всю сумму данных и методов предыдущих базовых классов. Если в предыдущем примере в классе Mammal будет замещена функция, описанная в классе Animals, то какой вариант функции получит класс Dog? Если класс Dog наследуется от класса Mammal, то он получит функцию в том виде, в каком она существует в классе Mammal, т.е. замещенную. Можно ли в производном классе описать как private функцию, которая перед этим была описана в базовом классе как public? Можно. Функция может быть не только защищена в производном классе, но и закрыта. Она останется закрытой для всех последующих классов, произведенных от этого. В каких случаях не следует делать функции класса виртуальными? Описание первой виртуальной функции вызовет создание v-таблицы, что потребует времени и дополнительной памяти. Последующее добавление виртуальных функций будет тривиальным. Многие программисты увлекаются созданием виртуальных функций и полагают, что если в программе есть уже одна виртуальная функция, то и все другие должны быть виртуальными. В действительности это не так. Создание виртуальных функций всегда должно отвечать решению конкретных задач. Предположим, что некоторая функция без параметров была описана в базовом классе как виртуальная, а затем перегружена таким образом, чтобы принимать один и два целочисленных параметра. Затем в производном классе был замещен вариант функции с одним целочисленным параметром. Что произойдет, если с помощью указателя, связанного с объектом производного класса, вызвать вариант функции с двумя параметрами? Замещение в производном классе варианта функции с одним параметром скроет от объектов этого класса все остальные варианты функции. Поэтому в случае обращения, описанного в вопросе, компилятор покажет сообщение об ошибке.
Коллоквиум
В этом разделе предлагаются вопросы для самоконтроля и укрепления полученных знаний и приводится несколько упражнений, которые помогут закрепить ваши практические навыки. Попытайтесь самостоятельно ответить на вопросы теста и выполнить задания, а потом сверьте полученные результаты с ответами в приложении Г. Не приступайте к изучению материала следующей главы, если для вас остались неясными хотя бы некоторые из предложенных ниже вопросов. Тест
1. Что такое v-таблица? 2. Что представляет собой виртуальный деструктор? 3. Можно ли объявить виртуальный конструктор? 4. Как создать виртуальный конструктор-копировщик? 5. Как вызвать функцию базового класса из объекта производного класса, если в производном классе эта функция была замещена? 6. Как вызвать функцию базового класса из объекта производного класса, если в производном классе эта функция не была замещена? 7. Если в базовом классе функция объявлена как виртуальная, а в производном классе виртуальность функции указана не была, сохранится ли функция как виртуальная в следующем произведенном классе? 8. С какой целью используется ключевое слово protected?
Упражнения
1. Объявите виртуальную функцию, которая принимает одно целочисленное значение и возвращает void. 2. Запишите объявление класса Square, произведенного от класса Rectangle, который, в свою очередь, произведен от класса Shape. 3. Предположим, что в предыдущем примере объект класса Shape не использует параметры, объект класса Rectangle принимает два параметра (length и width), а объект класса Square — один параметр (length); запишите конструктор для класса Square. 4. Запишите виртуальный конструктор-копировщик для класса Square, взятого из упражнения 3. 5. Жучки: что неправильно в следующем программном коде? void SomeFunction(Shape); Shape * pRect = new Rectangle; SoneFunction(*pRect); 6. Жучки: что неправильно в следующем программном коде? class Shape() { public: Shape(); virtual -Shape(); virtual Shape(const Shape&); };
|
||||
Последнее изменение этой страницы: 2016-12-10; просмотров: 325; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.144.95.167 (0.007 с.) |