C++类模板(1)
- 与函数模板类似,类也可以通过参数泛化,从而可以构建出一组不同型别的类实例(对象)
- 类模版实参可以是某一型别或常量(仅限int或enum)
C++类模板(2)
- 一个类模版的例子:<code>Stack<T></code>
const std::size_t DefaultStackSize = 1024;
template <typename T, std::size_t n = DefaultStackSize > class Stack {
public:
void Push(const T const& element);
int Pop(T& element);
int Top(T& element) const;
private:
std::vector<T> m_Members;
std::size_t m_nMaxSize = n;
};
- T可以是任意型别
- 模板实参也可以是一个int或enum型别的常量(此处是size_t,本质是int型别)
- n是编译时定义的常量
- n可以有默认值
- size_t型别的成员变量可以用n初始化
C++类模板(3)
- 类模板的声明
- 声明类模板与声明函数模板类似
- 关键字class和typename都可以用,但还是倾向于使用typename
template <typename T, std::size_t n> class Stack {...};
template <class T, std::size_t n> class Stack {...};
- 在类模板内部,T可以像其他型别一样(比如int,char等)定义成员变量和成员函数
void Push(const T const& element);
int Pop(T& element);
int Top(T& element) const;
std::vector<T> m_Members;};
C++类模板(4)
- 类模板的声明(续)
- 除了Copy constructor之外,如果在类模板中需要使用到这个类本身,比如定义operator=,那么应该使用其完整的定义(Stack<T>),而不是省略型别T。如下面的例子所示:
template <typename T, std::size_t n> class Stack
{
public:
...
Stack(Stack<T, n> const&); //copy constructor
Stack<T>& operator = (Stack<T,n> const&); //assignment operator
}
C++类模板(5)
- 类模板的实现
- 要定义一个类模板的成员函数,则要指明其是一个模板函数,例如,Push函数的定义应当如下:
template <typename T, std::size_t nMaxSize>
void Stack<T, nMaxSize>::Push(const T const& element)
{
if(m_Members.size() >= m_nMaxSize) {
//error handing...
return;
}
m_Members.push_back(element);
}
C++类模板(6)
- 类模板的实现(续)
- Pop函数:从Stack中弹出顶部元素
template <typename T, std::size_t nMaxSize>
int Stack<T, nMaxSize>::Pop(T& element)
{
if (m_Members.empty())
return 0;
element = m_Members.back(); //we have to first store the back element
m_Members.pop_back(); //because pop_back of a vector removes
return 1; //the last element but doesn't return it!
}
C++类模板(7)
- 类模板的实现(续)
- GetTop函数:获取Stack顶部元素,但没有Pop出该元素
template <typename T, std::size_t nMaxSize>
int Stack<T, nMaxSize>::GetTop(T& element) const
{
if (m_Members.empty())
return 0;
element = m_Members.back();
return 1;
}
C++类模板(8)
- 使用类模板
- <code>Stack<int> stack:</code>定义了一个型别为int的Stack,大小为默认值
- <code>Stack<int, 100> Stack:</code>定义了一个型别为int、大小为100的Stack
- 将100个元素Push到Stack中
for (int i = 0; i < 100; i+)
stack.Push(i);
- Pop出Stack顶部元素:
int element;
stack.Pop(element);
- 获取Stack顶部元素:
stack.GetTop(element);
- Stack的Stack定义:
Stack<Stack<int> > intStackStack
Stack<Stack<int>> intStackStack; //ERROR:>> is not allowed
C++类模板(9)
- 类模板特化(specializations)
- 允许对一个类模板的某些模板参数型别做特化
- 特化的作用或好处在于:
- 对于某种特殊的型别,可能可以做些特别的优化或提供不同的实现
- 避免在实例化类的时候引起一些可能产生的诡异行为
- 特化一个类模板的时候也意味着需要特化其所有参数化的成员函数
- 如果要特化一个类,那么做法是:
- 声明一个带template<>的类,即空参数列表
- 在类名称后面紧跟的尖括号中显式指明型别,例如:
template<>
class Stack<std::wstring> {
...
}
C++类模板(10)
- 类模板特化(specializations)(续)
- 特化后的具体实现可以和主模板的实现不一样,比如一下的特化增加了一个成员函数,并采用list作为元素存取的实现
template <> class Stack<std::wstring> {
public:
void SetStackSize(const std::size_t n) { m_nMaxSize = n; }//添加了一个新的成员函数
std::size_t CurrentSize() const { return m_Members.size(); }
void Push(const std::wstring const& element);
int Pop(std::wstring& element);
int GetTop(std::wstring& element) const;
private:
std::size_t m_nMaxSize;
std::list<std::wstring> m_Members;//采用list作为Stack的内部实现,取代了主模板中用vector实现的方式
}
C++类模板(11)
- 偏特化(Partial specialization)
- 类模板也可以被偏特化,比如主模板如果定义为:
template <typename T1, typename T2> class MyClass {...}; //Primary————1
- 可能产生以下几种对于主模板的偏特化:
- 将模板参数偏特化为同样型别:
template <typename T> class MyClass<T, T> {...}; ————2
- 将第二个模板参数偏特化为int型别,不再是泛型的T
template <typename T> class MyClass<T, int> {...}; ————3
- 将两个型别偏特化为指针:
template <typename T1, typename T2> class MyClass<T1, T2> {...}; ————4
C++类模板(12)
- 偏特化(Partial specialization)(续)
- 使用示例:
使用 |
原型 |
MyClass<int, float> obj; |
MyClass<T1, T2> ————1 |
MyClass<float, float> |
MyClass<T, T> ————2 |
MyClass<float, int> |
MyClass<T, int> ————3 |
MyClass<int, float> |
MyClass<T1, T2> ————4 |
- 如果有不止一个偏特化同等程度地能够匹配某个调用,那么该调用具有二义性。编译器不会通过编译:
Usage |
Prototype |
MyClass<int, int> obj; |
ERROR:matches MyClass<T, T> and MyClass<T, int> |
MyClass<int, int> obj; |
ERROR:matches MyClass<T, T> and MyClass<T1, T2> |
C++类模板(13)
- 默认模板实参
- 类似函数的默认参数,对于类模板而言也可以定义其模板参数的默认值,这些值就叫做默认模板实参
template <typename T, typename TContainer = std::vector<T> >//使用std::vector作为默认实参
class Stack {
private: TContainer m_Container;
...
};
- Stack<int> intStack:使用默认的vector作为实参
- Stack<std::wstring, std::list<std::wstring> > wstrStack:指定使用list作为容器而非默认的vector
C++类模板(14)
- 总结
- 模板类的性质是,有一个或多个型别未被指定
- 要使用一个模板类,就传入具体的型别作为实参;编译器会基于该型别来实例化类模板
- 对于类模板而言,只有被调用到的成员函数才会被实例化
- 类模板可以用特定的型别特化(specialization)
- 类模板也可以用特定的型别偏特化(partial specialization)
- 类模板参数可以有默认值
C++操作符重载(1)
- 关键字operator定义了一种特殊的函数,该函数的行为是将操作符应用于某一特定的型别,使之能够通过该操作符进行操作。比如,如果定义了string型别的operator+,那么连接两个字符串a和b的行为就可以用a+b进行操作
- 操作符重载给出了操作符的不同函数
- 编译器通过具体型别来识别某个操作符在该型别上的意义
- 本质上operator重载就是函数,即如果定义了string型别的Append函数,那么string型别的a+b和a.Append(b)是等价的
- 大多数内置的操作符支持重载,比如:!,!=,%,%=,&,&=,&&,||,(),,=,+,+=,-,-=,/,/=,,=,|,|=,<,<=,>,>=,=,==,<<,<<=,>>,>>=,~,[],new,delete
C++操作符重载(2)
- 操作符重载的一般规则
- 不可以用operator定义的一种新的操作符,比如**
- 对于内置型别(built-in type),不能再用operator重载
- 操作符重载的两种情况:
- 非静态成员函数,或
- 静态全局函数(如果该全局函数需要访问类的private或protected成员,则须声明为friend成员)
class ComplexType {
public:
//non-static member
ComplexType operator<(ComplexType&);
//global functions
friend ComplexType operator+(int, ComplexType&);
}
``
# C++操作符重载(3)
- 操作符重载的一般规则(续)
- 一元操作符(Unary operator)如果声明为成员函数,则没有参数;如果声明为全局函数则有一个参数
- 二元操作符(Binary operator)如果声明为成员函数,则有一个参数;如果声明为全局啊还是农户,则有两个参数
- 如果一个操作符既能够用作一元操作,又能够用作二元操作(比如:&,*,+,-),则可以分别被重载
- 操作符重载不能带有默认参数值
- 除了operator=,所有其他操作符重载均可以被子类继承
网友评论