####函数模板 C++ 中的模板分为函数模板和类模板两种 C++中,很多函数要求能够处理许多不同类型的数据,比如比较大小的函数,既要能够比较整数也要求能够比较字符串,这些算法处理的数据是不相同的,但是算法本身是相同的,这种情况下就可以使用函数模板来简化工作 示例代码:
template <typename T>
inline T const& Max(T const& a, T const& b) {
return a > b ? a : b;
}
T 表示一种抽象的数据类型,template 也可以写成 template ,译器会自动根据我们传入的标识符类型,来推导出相应的函数
调用形式:显示实例化或者隐式实例化(推导)
//test function teamplate
{
cout << "Max uint32_t ret = " << Max(10, 20) << endl;
//cout << "Max uint32_t ret = " << Max<uint32_t>(10, 20) << endl;
cout << "Max float ret = " << Max(1.0f, 2.1f) << endl;
//cout << "Max uint32_t ret = " << Max<float>(1.0f, 2.1f) << endl;
string s1 = "hello";
string s2 = "world";
//cout << "Max string ret = " << Max(s1, s2) << endl;
cout << "Max uint32_t ret = " << Max<string>(s1, s2) << endl;
}
####模板类 下面的代码定义了一个stack 的模板类,类中使用vector 存储数据
template <typename T> //template <class T> is also ok
class Stack {
public:
Stack(uint32_t size);
void push(T const&);
T pop();
T top();
bool empty();
private:
vector<T> mvec;
};
template <typename T>
Stack<T>::Stack(uint32_t size) {
mvec.reserve(size);
}
template <typename T>
void Stack<T>::push(T const& a) {
mvec.push_back(a);
}
template <typename T>
T Stack<T>::pop() {
auto p = mvec.back();
mvec.pop_back();
return p;
}
template <typename T>
T Stack<T>::top() {
auto p = mvec.back();
return p;
}
template <typename T>
bool Stack<T>::empty() {
if (mvec.size() == 0) {
return true;
} else {
return false;
}
}
注意点:
- 定义模板类中的成员函数时,需要加上模板定义的开头 template
- 模板类中的成员函数,用**Stack**指定类名
隐式实例化的调用形式:
Stack<string> mystack(10);
mystack.push("pushA");
mystack.push("pushB");
mystack.push("pushC");
mystack.push("pushD");
string p = mystack.pop();
cout << "mysatck pop element: " << p << endl;
cout << "mysatck pop element: " << mystack.top() << endl;
cout << "mysatck empty: " << mystack.empty() << endl;
#####模板类的显式实例化 通常情况下,模板类的定义和实现是放在一起的,在.h和.cpp 文件中,当然也可以将它们像普通类一样分开放; 因为模板类、函数通常定义在头文件中,这些头文件会被很多cpp文件包含,在这些cpp文件中会多次使用这些模板;编译完后的可执行程序中会包含多份模板类的定义,然而实际上,整个程序中却只有一份模板类的定义,这个处理是在编译和链接过程中实现的,目前主流的实现模式有两种
- Borland模式(先实例化模板类,再剔除)
- Cfront模式(存储在模板存储库) g++实现的是Borland 模式,由于我们为每一份实例化生成代码,这样在大型程序中就有可能包含很多重复的实例化定义代码,虽然链接阶段,链接器会剔除这些重复的定义,但仍然会导致编译过程中的目标文件(或者共享库文件)过于庞大。这时候,我们就可以通过C++11的模板显式实例化的方法解决
下面定义Stack 模板类,实现是在 templateDemo.cpp 中
#include <stdint.h>
#include <iostream>
#include "vector"
#include "templateDemo.h"
template class Stack<uint32_t>; //显式实例化
template class Stack<string>; //显式实例化
/* template stack demo */
template <typename T>
Stack<T>::Stack(uint32_t size) {
mvec.reserve(size);
}
template <typename T>
void Stack<T>::push(T const& a) {
mvec.push_back(a);
}
template <typename T>
T Stack<T>::pop() {
auto p = mvec.back();
mvec.pop_back();
return p;
}
template <typename T>
T Stack<T>::top() {
auto p = mvec.back();
return p;
}
template <typename T>
bool Stack<T>::empty() {
if (mvec.size() == 0) {
return true;
}
else {
return false;
}
}
templateDemo.h 定义模版类Stack
#ifndef _TEMPLATEDEMO_
#define _TEMPLATEDEMO_
#include <string>
#include <vector>
using namespace std;
// template function Max
template <typename T>
inline T const& Max(T const& a, T const& b) {
return a > b ? a : b;
}
// template function templateprint
template <typename T>
void templateprint(T& a) {
cout << "demo print value: " << a << endl;
}
// templaye class Stack
template <typename T> //template <class T> is also ok
class Stack {
public:
Stack(uint32_t size);
void push(T const&);
T pop();
T top();
bool empty();
private:
vector<T> mvec;
};
#endif
在templateDemo.cpp 显式实例化 Stack 类模板,在其他文件中就可以很自由的include templateDemo.h;当然也可以将模板类Stack 定义实现放在一起,可以避免使用显示实例化;
####C++ 运算符重载 运算符重载的本质还是函数重载,可以将运算符理解为特殊的函数名;
- 运算符重载时,运算顺序和优先级不变,操作数个数不变
- 不能创造新的操作符,并且规定下面的六个操作符不能重载
| 操作符 | 说明 |
|---|---|
| . | 类属关系操作符 |
| .* | 成员指针操作符 |
| :: | 作用域操作符 |
| ?: | 条件运算操作符 |
| # | 预编译处理符号 |
| sizeof | 取数据长度操作符 |
可以重载的运算符基本可以分为:
- 赋值运算符 =
- 关系运算符 > 、< 、>= 、==、!=、<=
- 算术运算符 +、++、–、- -、+=、-=、/、*
- 其他特殊的运算符 <<、>>、[ ]、() 定义一个演示操作符重载的类:
class complexDemoClass {
public:
inline complexDemoClass(double a, double b) :r(a), t(b) {
cout << "construct complexDemoClass (a,b)" << endl;
}
inline complexDemoClass() {
cout << "construct complexDemoClass nopara" << endl;
}
inline ~complexDemoClass() {
cout << "destruct ~complexDemoClass" << endl;
}
...... // define all operator function
inline void dumpClass() {
cout << "dumpClass r = " << r << " t = " << t << endl;
}
private:
double r;
double t;
}
######赋值运算符重载 基本形式为:模板形式为 classname& operator=(classname& 实例对象) 注意输入参数只有一个,而且必须是对象的引用,返回值也必须是对象的引用 tips:可以将operator= 看成一个整体,当成函数名
complexDemoClass& operator=(complexDemoClass& p) {
//cout << "Run" << __FUNCTION__ << endl;
r = p.r;
t = p.t;
return *this;
}
调用形式如下,注意传递的是引用,所以不可以将指针作为参数传入
complexDemoClass democlass(1.0f, 1.0f);
complexDemoClass democlassb(1.3f, 1.3f);
democlass = democlassb;
//democlass.dumpClass();
complexDemoClass* pclassc = new complexDemoClass(2.0f, 2.0f);
//democlass = *pclassc;
//democlass.dumpClass();
######算术运算符重载 算术运算符包括+、++、–、- -、+=、-=、/、*等,基本模板形式为: classname& operator运算符(classname& 实例对象) 除了自增和自减运算符,其余算术运算符都是一个参数,必须是对象的引用,返回值也是对象的引用
只要语句中使用到了下面的算术运算符都会触发运算符重载函数,如果要接收返回值,可以再定义一个引用,又或者定义一个相应的类型实例去接收,此时实际调用到了对应类型拷贝构造函数
下面的运算符可以直接重载,重载函数可以不属于任何一个类,下面两种格式都是支持的:
- 全局运算符重载 friend classname& operator运算符(const classname& a,const classname& b)
- 成员函数重载 classname& operator运算符(const classname& b)
//有点疑问?是将栈上的变量返回
inline complexDemoClass& operator+(complexDemoClass& p) {
return complexDemoClass(r + p.r, t + p.t);
}
inline complexDemoClass& operator-(complexDemoClass& p) {
return complexDemoClass(r - p.r, t - p.t);
}
inline complexDemoClass& operator*(complexDemoClass& p) {
return complexDemoClass(r*p.r, t*p.t);
}
inline complexDemoClass& operator/(complexDemoClass& p) {
return complexDemoClass(r / p.r, t / p.t);
}
inline complexDemoClass& operator+=(complexDemoClass& p) {
r += p.r;
t += p.t;
return *this;
}
inline complexDemoClass& operator-=(complexDemoClass& p) {
r -= p.r;
t -= p.t;
return *this;
}
inline complexDemoClass& operator++(void) {
r += 1.0f;
t += 1.0f;
return *this;
}
inline complexDemoClass& operator--(void) {
r -= 1.0f;
t -= 1.0f;
return *this;
}
注意算术运算符+,-,*,/ 的调用形式如下 不可以使用 complexDemoClass democ; democ = demoa + demob;的形式,此形式又会调用到赋值运算符重载
complexDemoClass democ 此时已经实例化了对象
complexDemoClass demoa(1.0f, 1.0f);
complexDemoClass demob(2.0f, 2.0f);
complexDemoClass democ = demoa + demob;
democ.dumpClass();
complexDemoClass demod = demoa - demob;
demod.dumpClass();
complexDemoClass demoe = demoa * demob;
demoe.dumpClass();
complexDemoClass demof = demoa / demob;
demof.dumpClass();
+,-,*,/ 重载函数调用完成之后,两个输入对象本身没有变化
+=,-= 运算符重载的定义和调用形式为:
inline complexDemoClass& operator+=(complexDemoClass& p) {
r += p.r;
t += p.t;
return *this;
}
inline complexDemoClass& operator-=(complexDemoClass& p) {
r -= p.r;
t -= p.t;
return *this;
}
调用形式:
{
complexDemoClass demoa(1.0f, 1.0f);
complexDemoClass demob(2.0f, 2.0f);
demoa += demob;
//demoa.dumpClass();
demoa -= demob;
//demoa.dumpClass();
}
注意使用 +=,-= 需要定义在类的内部,本质上对象的属性本身被操作符改变了
++ -- 运算符重载(TBD)
#####关系运算符重载 基本模板形式为:
- 全局运算符重载 friend bool& operator运算符(const classname& a,const classname& b)
- 成员函数重载 bool& operator运算符(const classname& a)
成员函数重载实例:
inline bool operator>(const complexDemoClass& a) {
bool ret = (r > a.r) && (t > a.t);
return ret;
}
inline bool operator<(const complexDemoClass& a) {
bool ret = (r < a.r) && (t < a.t);
return ret;
}
inline bool operator==(const complexDemoClass& a) {
bool ret = (r == a.r) && (t == a.t);
return ret;
}
inline bool operator!=(const complexDemoClass& a) {
bool ret = (r != a.r) && (t != a.t);
return ret;
}
调用形式为:
{
complexDemoClass demoa(1.0f, 1.0f);
complexDemoClass demob(2.0f, 2.0f);
bool ret = (demoa == demob);
cout << "demoa == demob:" << ret << endl;
ret = (demoa != demob);
cout << "demoa != demob:" << ret << endl;
cout << "demoa > demob:" << (demoa > demob) << endl;
cout << "demoa < demob:" << (demoa < demob) << endl;
}