Д. Стефенс - C++. Сборник рецептов
- Название:C++. Сборник рецептов
- Автор:
- Жанр:
- Издательство:КУДИЦ-ПРЕСС
- Год:2007
- Город:Москва
- ISBN:5-91136-030-6
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Д. Стефенс - C++. Сборник рецептов краткое содержание
Данная книга написана экспертами по C++ и содержит готовые рецепты решения каждодневных задач для программистов на С++. Один из авторов является создателем библиотеки Boost Iostreams и нескольких других библиотек C++ с открытым исходным кодом. В книге затрагивается множество тем, вот лишь некоторые из них: работа с датой и временем; потоковый ввод/вывод; обработка исключений; работа с классами и объектами; сборка приложений; синтаксический анализ XML-документов; программирование математических задач. Читатель сможет использовать готовые решения, а сэкономленное время и усилия направить на решение конкретных задач.
C++. Сборник рецептов - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Используйте шаблон метода и объявите параметр шаблона для типа объекта. Небольшая иллюстрация приведена в примере 8.13.
Пример 8.13. Использование шаблона метода
class ObjectManager {
public:
template T* gimmeAnObject();
template
void gimmeAnObject(T*& p);
};
template
T* ObjectManager::gimmeAnObject() {
return(new T);
}
template
void ObjectManager::gimmeAnObject(T*& p) {
p = new T;
}
class X { /*...*/ };
class Y { /* ... */ };
int main() {
ObjectManager om;
X* p1 = om.gimmeAnObject(); // Требуется указать параметр
Y* p2 = om.gimmeAnObject(); // шаблона
om.gimmeAnObject(p1); // Однако не здесь, так как компилятор может
om.gimmeAnObject(p2); // догадаться о типе T по аргументам
}
При обсуждении шаблонов функций или классов слова «параметр» и «аргумент» становятся несколько двусмысленными. Имеется по два вида каждого: шаблона и функции. Параметры шаблона — это параметры в угловых скобках, например T
в примере 8.13, а параметры функции — это параметры в обычном смысле.
Рассмотрим класс ObjectManager
из примера 8.13. Это упрощенная версия шаблона фабрики, описанного в рецепте 8.2, так что мне потребовалось объявить метод gimmeAnObject
, который создает новые объекты и который клиентский код сможет использовать вместо непосредственного обращения к new
. Это можно сделать, либо возвращая указатель на новый объект, либо изменяя указатель, переданный в метод клиентским кодом. Давайте посмотрим на каждый из этих подходов.
Объявление шаблона метода требует, чтобы было использовано ключевое слово template
и были указаны параметры шаблона.
template T* gimmeAnObject();
template void gimmeAnObject(T*& p);
Оба этих метода используют в качестве параметра шаблона T
, но они не обязаны это делать. Каждый из них представляет параметр шаблона только для данного метода, так что их имена не связаны друг с другом. То же самое требуется сделать для определения этих шаблонов методов, т.е. использовать это же ключевое слово и перечень параметров шаблона. Вот как выглядят мои определения.
template
T* ObjectManager.:gimmeAnObject() {
return(new T);
}
template
void ObjectManager::gimmeAnObject(T*& p) {
p = new T;
}
Теперь есть пара способов вызвать эти шаблоны методов. Во-первых, их можно вызвать явно, используя параметры шаблона, как здесь.
X* p1 = om.gimmeAnObject();
X
— это имя некоего класса. Либо можно позволить компилятору догадаться об аргументах параметров шаблона, передав в методы аргументы типа (типов) параметров шаблона. Например, можно вызвать вторую форму gimmeAnObject
, не передавая ей ничего в угловых скобках.
om.gimmeAnObject(p1);
Это работает благодаря тому, что компилятор может догадаться о T
, посмотрев на p1
и распознав, что он имеет тип X*
. Такое поведение работает только для шаблонов функций (методов или отдельных) и только тогда, когда параметры шаблона понятны из аргументов функции .
Шаблоны методов не имеют большой популярности при разработке на C++, но время от времени они оказываются очень полезны, так что следует знать, как создавать их. Я часто сталкиваюсь с необходимостью сдерживать себя, когда мне хочется использовать метод, который бы работал с типами, которые не связаны друг с другом механизмом наследования. Например, если есть метод foo
, который должен принимать один аргумент, который всегда будет классом, наследуемым от некоторого базового класса, то шаблон не требуется: здесь можно просто сделать параметр типа базового класса или ссылки. После этого этот метод будет прекрасно работать с параметром, имеющим тип любого подкласса; это обеспечивается самим C++.
Но может потребоваться функция, которая работает с параметрами, которые не наследуются от одного и того же базового класса (или классов). В этом случае можно либо написать несколько раз один и тот же метод — по одному разу для каждого из типов, либо сделать его шаблоном метода. Использование шаблонов также позволяет использовать специализацию, предоставляющую возможность создавать реализации шаблонов для определенных аргументов шаблона. Но это выходит за рамки одного рецепта, так что сейчас я прекращаю обсуждение, но это мощная методика, поэтому при использовании программирования шаблонов не забудьте про такую возможность.
Рецепт 8.11.
8.13. Перегрузка операторов инкремента и декремента
Имеется класс, для которого имеют смысл операции инкремента и декремента, и требуется перегрузить operator++
и operator--
, которые позволят легко и интуитивно выполнять инкремент и декремент объектов этого класса.
Чтобы это сделать, перегрузите префиксную и постфиксную формы ++
и --
. Пример 8.14 показывает обычную методику перегрузки операторов инкремента и декремента.
Пример 8.14. Перегрузка инкремента и декремента
#include
using namespace std;
class Score {
public:
Score() : score_(0) {}
Score(int i) : score_(i) {}
Score& operator++() {
// префикс
++score_;
return(*this);
}
const Score operator++(int) {
// постфикс
Score tmp(*this);
++(*this); // Использование префиксного оператора
return(tmp);
}
Score& operator--() {
--score_;
return(*this);
}
const Score operator--(int x) {
Score tmp(*this);
--(*this);
return(tmp);
}
int getScore() const {return(score_);}
private:
int score_;
};
int main() {
Score player1(50);
player1++;
++player1; // score = 52
cout << "Счет = " << player1.getScore() << '\n';
(--player1)--; // score_ = 50
cout << "Счет = " << player1.getScore() << '\n';
}
Операторы инкремента и декремента часто имеют смысл для классов, которые представляют некоторые разновидности целых значений. Если вы понимаете разницу между префиксной и постфиксной формами и следуете соглашениям о возвращаемых значениях, то их легко использовать.
Представьте себе инкремент целого числа. С помощью оператора ++
имеется два способа выполнить его для некоторого целого i
.
i++; // постфиксный
++i; // префиксный
Оба инкрементируют i
: первая версия создает временную копию i
, инкрементирует i
и затем возвращает временное значение, а вторая инкрементирует i
и затем возвращает его. C++ позволяет выполнять перегрузку операторов, что означает, что вы можете заставить свой собственный тип (класс или enum
) вести себя так же, как и int
.
Интервал:
Закладка: