Чтение онлайн

на главную - закладки

Жанры

Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ

Майерс Скотт

Шрифт:

Top *pt1 = new Middle; // преобразует Middle* в Top*

Top *pt2 = new Bottom; // преобразует Middle* в Bottom*

Const Top *pct2 = pt1; // преобразует Top* в const Top*

Эмулировать такие преобразования с помощью определяемых пользователем «интеллектуальных» указателей не просто. Для этого нужно, чтобы компилировался такой код:

Template<typename T>

class SmartPtr {

public:

explicit SmartPtr(T *realPtr); // интеллектуальные указатели обычно

... //
инициализируются встроенными

}; // указателями

SmartPtr<Top> pt1 = // преобразует SmartPtr<Middle>

SmartPtr<Middle>(new Middle); // в SmartPtr<Top>

SmartPtr<Top> pt2 = // преобразует SmartPtr<Bottom>

SmartPtr<Bottom>(new Bottom); // SmartPtr<Top>

SmartPtr<const Top> pct2 = pt1;

Разные конкретизации одного шаблона не связаны каким-либо отношением, поэтому компилятор считает, что SmartPtr<Middle> и SmartPtr<Top> – совершенно разные классы, не более связанные друг с другом, чем, например, vector<float> и Widget. Чтобы можно было осуществлять преобразования между разными классами SmartPtr, необходимо явно написать соответствующий код. В приведенном выше примере каждое предложение создает новый объект интеллектуального указателя, поэтому для начала сосредоточимся на написании конструкторов, которые будут вести себя так, как нам нужно. Ключевое наблюдение состоит в том, что невозможно написать сразу все необходимые конструкторы. В приведенной иерархии мы можем сконструировать SmartPtr<Top> из SmartPtr<Middle> или SmartPtr<Bottom>, но если в будущем иерархия будет расширена, то придется добавить возможность конструирования объектов SmartPtr<Top> из других типов интеллектуальных указателей. Например, если мы позже добавим такой класс:

class BelowBottom: public Bottom {...};

то нужно будет поддержать создание объектов SmartPtr<Top> из SmartPtr<Below-Bottom>, и, очевидно, не хотелось бы ради этого модифицировать шаблон SmartPtr.

В принципе, нам может понадобиться неограниченное число конструкторов. Поскольку шаблон может быть конкретизирован для генерации неограниченного числа функций, похоже, что нам нужен не конструктор-функция для SmartPtr, а конструктор-шаблон. Это пример шаблона функции-члена (часто называемого шаблонного члена), то есть шаблона, генерирующего функции-члены класса:

template<typename T>

class SmartPtr {

public:

template<typename U> // шаблонный член

SmartPtr(const SmartPtr<U>& other); // для «обобщенного

... // конструктора копирования»

};

Здесь говорится, что для каждой пары типов T и U класс SmartPtr<T> может быть создан из SmartPtr<U>, потому что SmartPtr<T> имеет конструктор, принимающий параметр типа SmartPtr<U>. Подобные конструкторы, создающие один объект из другого, тип которого является другой конкретизацией того же шаблона (например, SmartPtr<T> из SmartPtr<U>), иногда называют обобщенными конструкторами копирования.

Обобщенный конструктор копирования в приведенном выше примере не объявлен с модификатором explicit. И это сделано намеренно.

Преобразования типов между встроенными типами указателей (например, из указателя на производный класс к указателю на базовый класс) происходят неявно и не требуют приведения, поэтому разумно и для интеллектуальных указателей эмулировать такое поведение. Именно поэтому и не указано слово explicit в объявлении обобщенного конструктора шаблона.

Будучи объявлен описанным выше образом, обобщенный конструктор копирования для SmartPtr предоставляет больше, чем нам нужно. Да, мы хотим иметь возможность создавать SmartPtr<Top> из SmartPtr<Bottom>, но вовсе не просили создавать SmartPtr<Bottom> из SmartPtr<Top>, потому что это противоречит смыслу открытого наследования (см. правило 32). Мы также не хотим создавать SmartPtr<int> из SmartPtr<double>, потому что не существует неявного преобразования int* в double*. Каким-то образом мы должны сузить многообразие функций-членов, которые способен генерировать этот шаблон.

Предполагая, что SmartPtr написан по образцу auto_ptr и tr1::shared_ptr, то есть предоставляет функцию-член get, которая возвращает копию встроенного указателя, хранящегося в объекте «интеллектуального» указателя (см. правило 15), мы можем воспользоваться реализацией шаблонного конструктора, чтобы ограничить набор преобразований:

template<typename T>

class SmartPtr {

public:

template<typename U>

SmartPtr(const SmartPtr<U>& other) // инициировать этот хранимый

:heldPtr(other.get) {...} // указатель указателем, хранящимся

// в другом объекте

T *get const { return heldPtr;}

...

private: // встроенный указатель,

T *heldPtr; // хранящийся в «интеллектуальном»

};

Мы используем список инициализации членов, чтобы инициализировать член данных SmartPtr<T> типа T* указателем типа U*, который хранится в Smart-Ptr<U>. Этот код откомпилируется только тогда, когда существует неявное преобразование указателя U* в T*, а это как раз то, что нам нужно. Итак, SmartPtr<T> теперь имеет обобщенный копирующий конструктор, который компилируется только тогда, когда ему передается параметр совместимого типа.

Использование шаблонных функций-членов не ограничивается конструкторами. Еще одно полезное применение таких функций – поддержка присваивания. Например, класс shared_ptr из TR1 (см. правило 13) поддерживает конструирование из всех совместимых встроенных указателей, tr1::shared_ptr, auto_ptr и tr1::weak_ptr (см. правило 54), а также наличие в правой части оператора присваивания объекта любого из этих типов, кроме tr1::weak_ptr. Ниже приведен фрагмент спецификации TR1 для tr1::shared_ptr; обратите внимание, что при объявлении параметров шаблона используется ключевое слов class, а не typename. Как объясняется в правиле 42, в данном контексте они означают одно и то же.

template<class T> class shared_ptr {

public:

template<class Y> // конструирует из

explicit shared_ptr(Y *p); // любого совместимого

template<class Y> // встроенного указателя,

shared_ptr(shared_ptr<Y> const& r); // shared_ptr,

template<class Y> // weak_ptr или

explicit shared_ptr(weak_ptr<Y> const& r); // auto_ptr

Поделиться:
Популярные книги

Моя на одну ночь

Тоцка Тала
Любовные романы:
современные любовные романы
короткие любовные романы
5.50
рейтинг книги
Моя на одну ночь

Черный Маг Императора 8

Герда Александр
8. Черный маг императора
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Черный Маг Императора 8

Измена. Отбор для предателя

Лаврова Алиса
1. Отбор для предателя
Фантастика:
фэнтези
5.00
рейтинг книги
Измена. Отбор для предателя

Кодекс Крови. Книга II

Борзых М.
2. РОС: Кодекс Крови
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Крови. Книга II

Шаг в бездну

Муравьёв Константин Николаевич
3. Перешагнуть пропасть
Фантастика:
фэнтези
космическая фантастика
7.89
рейтинг книги
Шаг в бездну

Часовая битва

Щерба Наталья Васильевна
6. Часодеи
Детские:
детская фантастика
9.38
рейтинг книги
Часовая битва

Вечная Война. Книга II

Винокуров Юрий
2. Вечная война.
Фантастика:
юмористическая фантастика
космическая фантастика
8.37
рейтинг книги
Вечная Война. Книга II

Хроники странного королевства. Вторжение. (Дилогия)

Панкеева Оксана Петровна
110. В одном томе
Фантастика:
фэнтези
9.38
рейтинг книги
Хроники странного королевства. Вторжение. (Дилогия)

Часовой ключ

Щерба Наталья Васильевна
1. Часодеи
Фантастика:
фэнтези
9.36
рейтинг книги
Часовой ключ

Инвестиго, из медика в маги

Рэд Илья
1. Инвестиго
Фантастика:
фэнтези
городское фэнтези
попаданцы
5.00
рейтинг книги
Инвестиго, из медика в маги

Кротовский, может, хватит?

Парсиев Дмитрий
3. РОС: Изнанка Империи
Фантастика:
попаданцы
альтернативная история
аниме
7.50
рейтинг книги
Кротовский, может, хватит?

Драконий подарок

Суббота Светлана
1. Королевская академия Драко
Любовные романы:
любовно-фантастические романы
7.30
рейтинг книги
Драконий подарок

Очешуеть! Я - жена дракона?!

Амеличева Елена
Фантастика:
юмористическая фантастика
5.43
рейтинг книги
Очешуеть! Я - жена дракона?!

Идеальный мир для Лекаря 9

Сапфир Олег
9. Лекарь
Фантастика:
боевая фантастика
юмористическое фэнтези
6.00
рейтинг книги
Идеальный мир для Лекаря 9