Программирование >>  Оптимизация возвращаемого значения 

1 ... 39 40 41 [ 42 ] 43 44 45 ... 96


конструкторы полезны в различных обстоятельствах, например при чтении данных объекта с диска (или по сети, или с накопителя на магнитной ленте и т.д.).

Широко используется также разновидность виртуального конструктора -виртуальный конструктор копирования; он возвращает указатель на новую копию объекта, вызывающего функцию. Из-за такого поведения виртуальные конструкторы копирования обычно имеют имена типа copySelf, cloneSelf или просто clone, как в следующем примере. Немного найдется функций, которые были бы реализованы столь же просто.

class NLComponent { public:

Объявление виртуального конструктора копирования, virtual NLComponent * clone() const = 0;

class TextBlock: public NLComponent { public:

virtual TextBlock * clone() const Виртуальный

{ return new TextBlock (*this) ; } конструктор

копирования.

class Graphic: public NLComponent { public:

virtual Graphic * clone() const Виртуальный

{ return new Graphic (*this) ; } конструктор

копирования.

Как видите, виртуальный конструктор копирования просто вызывает реальный конструктор копирования объекта. Значение копирования , следовательно, одинаково в обеих функциях. Если реальный конструктор копирования выполняет простое копирование, то же самое делает и виртуальный конструктор копирования. Если реальный конструктор выполняет сложное копирование, аналогично поступает и виртуальный. Если реальный конструктор копирования осуществляет какие-то особые действия, например подсчет ссылок или копирование по записи (см. правило 29), то же самое делает и виртуальный конструктор. Совместимость - замечательная вещь.

Обратите внимание, что приведенная выше реализация использует преимущества ослабленных ограничений на тип возвращаемого виртуальной функцией значения, которые были приняты относительно недавно. Переопределенная в производном классе виртуальная функция базового класса больше не должна объявлять тот же самый тип возвращаемого значения. Вместо этого, если функция возвращает указатель (или ссылку) на базовый класс, то функция производного класса может возвращать указатель (или ссылку) на класс, производный от базового. Это не приводит к появлению дыр в системе типов С++ и позволяет корректно объявлять такие функции, как виртуальные конструкторы копирования. Поэтому виртуальный конструктор копирования clone объекта TextBlock может



возвращать указатель TextBlock*, а виртуальный конструктор копирования clone объекта Graphic - указатель Graphic*, хотя функция clone объекта NLGomponent возвращает указатель NLGomponent*.

Существование виртуального конструктора копирования в объекте NLGomponent позволяет легко реализовать конструктор копирования (обычный) для объекта NewsLetter:

class NewsLetter { public:

NewsLetter(const NewsLetter& rhs) ;

private:

list<NLComponent*> components;

NewsLetter::NewsLetter(const NewsLetter& rhs) {

/ / Итерация no списку rhs, для копирования элемента в список блоков объекта используется его виртуальный конструктор копирования. Детали работы этого кода обсуждаются в правиле 35.

for (list<NLComponent*>::const iterator it = rhs.components.begin(); it != rhs.components.endO; ++it) {

it указывает на текущий элемент rhs . components ,

поэтому для получения копиии вызывается

его функция clone, и полученный дубликат добавляется

в конец списка блоков этого объекта.

components.push back((*it)->clone());

Если вы не знакомы со стандартной библиотекой шаблонов, этот код может показаться вам странным, но основная его идея проста: обходится список блоков для копируемого объекта NewsLetter, и для каждого блока вызывается его виртуальный конструктор копирования. В данном сл5Д1ае вам нужен виртуальный конструктор копирования, так как список содержит указатели на объекты NLGomponent, хотя на самом деле каждый из указателей ссылается на объект типа TextBlock или Graphic. Необходимо скопировать то, на что ссылается указатель, и сделать это можно при помощи виртуального конструктора копирования.

Виртуализация функций - не членов класса

Как и конструкторы, функции - не члены класса в действительности не могут быть виртуальными. Однако так же, как имеет смысл представлять виртуальными функции, создающие новые объекты разных типов, точно так же имеет смысл представлять виртуальными функции - не члены класса, поведение которых зависит от динамического типа их параметров. Предположим, например, что вы хотите реализовать операторы вывода для классов TextBlock и Graphic. Очевидный



подход к решению подобной задачи состоит в том, чтобы сделать операторы вывода виртуальными. Но оператор вывода - это operator , а левым аргументом данной функции является ostream&, что не позволяет сделать его функцией -членом класса TextBlock или Graphic.

Если попытаться обойти запрет, посмотрите, что произойдет:

class NLComponent { public:

Необычное объявление оператора вывода.

vitual ostream& operator (ostream& str) const = 0;

class TextBloctc, : public NLComponent { public:

Виртуальный оператор вывода (также не принято) . virtual ostream& operator (ostream& str) const;

class Graphic: public NLComponent { public:

Виртуальный оператор вывода (также не принято) . virtual ostreamSc operator (ostream& str) const;

TextBloclt t; Graphic g;

t cout; Вывести t в cout при помощи

виртуального operator<<

(необычный синтаксис). g cout; Вывести t в cout при помощи

виртуального operator

(необычный синтаксис) .

Клиенты должны помещать объект потока справа от символа , что противоречит соглашению для операторов вывода. Чтобы вернуться к обычному синтаксису, придется вывести operator из классов TextBlock и Graphic, но если делать это, нельзя будет объявлять его как виртуальный.

Альтернативный подход - объявить виртуальную функцию для вывода (например, функцию print) и определить ее в классах TextBlock и Graphic. Но в этом cnjae синтаксис вывода объектов TextBlock и Graphic будет не совпадать с синтаксисом остальных типов языка, использующих operator в качестве оператора вывода.

Ни одно из предложенных решений не является удовлетворительным. Вам нужно, чтобы функция - не член класса вызывала operator , который вел бы себя подобно виртуальной функции, такой как printf. Обратите внимание: описание того, что нужно, очень близко к описанию того, как это можно сделать. Необходимо определить обе функции operator и print и вызвать вторую из первой!

class NLComponent { public:



1 ... 39 40 41 [ 42 ] 43 44 45 ... 96

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика