Объявление шаблона метода требует, чтобы было использовано ключевое слово
template
и были указаны параметры шаблона.
template<typename T> T* gimmeAnObject;
template<typename T> void gimmeAnObject(T*& p);
Оба этих метода используют в качестве параметра шаблона
T
, но они не обязаны это делать. Каждый из них представляет параметр шаблона только для данного метода, так что их имена не связаны друг с другом. То же самое требуется сделать для определения этих шаблонов методов, т.е. использовать это
же ключевое слово и перечень параметров шаблона. Вот как выглядят мои определения.
template<typename T>
T* ObjectManager.:gimmeAnObject {
return(new T);
}
template<typename T>
void ObjectManager::gimmeAnObject(T*& p) {
p = new T;
}
Теперь есть пара способов вызвать эти шаблоны методов. Во-первых, их можно вызвать явно, используя параметры шаблона, как здесь.
X* p1 = om.gimmeAnObject<X>;
X
— это имя некоего класса. Либо можно позволить компилятору догадаться об аргументах параметров шаблона, передав в методы аргументы типа (типов) параметров шаблона. Например, можно вызвать вторую форму
gimmeAnObject
, не передавая ей ничего в угловых скобках.
om.gimmeAnObject(p1);
Это работает благодаря тому, что компилятор может догадаться о
T
, посмотрев на
p1
и распознав, что он имеет тип
X*
. Такое поведение работает только для шаблонов функций (методов или отдельных) и только тогда, когда параметры шаблона понятны из аргументов функции.
Шаблоны методов не имеют большой популярности при разработке на C++, но время от времени они оказываются очень полезны, так что следует знать, как создавать их. Я часто сталкиваюсь с необходимостью сдерживать себя, когда мне хочется использовать метод, который бы работал с типами, которые не связаны друг с другом механизмом наследования. Например, если есть метод
foo
, который должен принимать один аргумент, который всегда будет классом, наследуемым от некоторого базового класса, то шаблон не требуется: здесь можно просто сделать параметр типа базового класса или ссылки. После этого этот метод будет прекрасно работать с параметром, имеющим тип любого подкласса; это обеспечивается самим C++.
Но может потребоваться функция, которая работает с параметрами, которые не наследуются от одного и того же базового класса (или классов). В этом случае можно либо написать несколько раз один и тот же метод — по одному разу для каждого из типов, либо сделать его шаблоном метода. Использование шаблонов также позволяет использовать специализацию, предоставляющую возможность создавать реализации шаблонов для определенных аргументов шаблона. Но это выходит за рамки одного рецепта, так что сейчас я прекращаю обсуждение, но это мощная методика, поэтому при использовании программирования шаблонов не забудьте про такую возможность.
Смотри также
Рецепт 8.11.
8.13. Перегрузка операторов инкремента
и декремента
Проблема
Имеется класс, для которого имеют смысл операции инкремента и декремента, и требуется перегрузить
operator++
и
operator--
, которые позволят легко и интуитивно выполнять инкремент и декремент объектов этого класса.
Решение
Чтобы это сделать, перегрузите префиксную и постфиксную формы
++
и
– -
. Пример 8.14 показывает обычную методику перегрузки операторов инкремента и декремента.
Пример 8.14. Перегрузка инкремента и декремента
#include <iostream>
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';
}
Обсуждение
Операторы инкремента и декремента часто имеют смысл для классов, которые представляют некоторые разновидности целых значений. Если вы понимаете разницу между префиксной и постфиксной формами и следуете соглашениям о возвращаемых значениях, то их легко использовать.
Представьте себе инкремент целого числа. С помощью оператора
++
имеется два способа выполнить его для некоторого целого