泛型(Generic Programming)便是指具备在多种数据类型上皆可操做的含意。 泛型编程的表明做品STL是一种高效、泛型、可交互操做的软件组件。
泛型编程最初诞生于C++中,目的是为了实现C++的STL(标准模板库)。其语言支持机制就是模板(Templates)。模板的核心思想是参数化类型,即把一个本来特定于某个类型的算法或类当中的类型信息抽掉,抽出来作成模板参数T。ios
定义一个交换两个数的宏算法
#define SWAP(t, a, b) \ do \ { \ t c = a; \ a = b; \ b = c; \ }while(0);
宏代码块实现的优势是代码复用,适合全部类型;缺点是缺乏类型检查。编程
#include <iostream> using namespace std; void swap(int &a, int& b) { int t = a; a = b; b = t; } void swap(double &a,double b) { double t = a; a = b; b = t; } int main() { int ia = 10; int ib = 20; swap(ia,ib); cout<<ia<<ib<<endl; double da = 10, db = 20; swap(da,db); cout<<da<<db<<endl; return 0; }
函数重载实现的优势是真正进行函数调用,C++编译器进行类型检查;缺点是根据类型重复定义函数,没法代码复用。数组
函数模板是可用不一样类型进行调用的特殊函数,关键在于类型参数化。
函数模板的语法格式以下:数据结构
template<typename/class 类型参数表> 返回类型 函数模板名(函数参数列表) { 函数模板定义体 }
template关键字用于声明开始进行泛型编程。
typename关键字用于声明泛指类型。
函数模板能够自动推导类型进行调用,也能够显示指定具体类型进行调用。ide
#include <iostream> using namespace std; template <typename T> void Swap(T& a,T &b ) { T t = a; a = b; b = t; } int main() { int ia = 10; int ib = 20; Swap(ia,ib); //Swap<int>(ia,ib); cout<<ia<<ib<<endl; double da = 10, db = 20; Swap(da,db); //Swap<double>(da,db); cout<<da<<db<<endl; string sa ="china"; string sb = "America"; Swap(sa,sb); cout<<sa<<sb<<endl; return 0; }
判断一个变量是否是指针类型示例:函数
template <typename T> bool isPtr(T *p) { return true; } template <typename T> bool isPtr(T t) { return false; }
函数模板,只适用于函数的参数个数相同而类型不一样,且函数体相同的状况。若是个数不一样,则不能用函数模板。学习
C++编译器从函数模板经过具体类型产生不一样的函数,C++编译器会对函数模板进行两次编译,一次是函数模板代码进行编译,一次是参数替换后的函数代码进行编译。this
#include <iostream> using namespace std; template <typename T> void Swap(T& a, T& b) { T c = a; a = b; b = c; } class Test { }; typedef void (*pFuncInt)(int&, int&); typedef void (*pFuncDouble)(double&, double&); typedef void (*pFuncTest)(Test&, Test&); int main(int argc, char *argv[]) { pFuncInt pi = Swap;//Swap<int> printf("0x%x\n", pi); pFuncDouble pd = Swap;//Swap<double> printf("0x%x\n", pd); pFuncTest pt = Swap;//Swap<Test> printf("0x%x\n", pt); return 0; }
函数模板自己不容许隐式类型转换,所以,自动推导类型时须要严格匹配,但当显示指定类型参数时能够进行隐式类型转换。
函数模板中的返回值类型必须显示指定。spa
add<int>(ia,ib);
函数模板能够定义多个不一样的类型参数,但没法自动推导返回值类型,能够从左向右部分指定类型参数,实际工程中将返回值做为第一个类型参数,必须显式指定。
#include <iostream> #include <string> using namespace std; template < typename T1, typename T2, typename T3 > T1 Add(T2 a, T3 b) { return static_cast<T1>(a + b); } int main() { // T1 = int, T2 = double, T3 = double int r1 = Add<int>(0.5, 0.8); // T1 = double, T2 = float, T3 = double double r2 = Add<double, float>(0.5, 0.8); // T1 = float, T2 = float, T3 = float float r3 = Add<float, float, float>(0.5, 0.8); cout << "r1 = " << r1 << endl; // r1 = 1 cout << "r2 = " << r2 << endl; // r2 = 1.3 cout << "r3 = " << r3 << endl; // r3 = 1.3 return 0; }
函数模板能够被重载,C++编译器优先考虑普通函数,但若是函数模板能够产生更好的匹配,则使用函数模板,能够经过空模板实参列表限定只能使用函数模板。
#include <iostream> #include <string> using namespace std; template < typename T > T Max(T a, T b) { cout << "T Max(T a, T b)" << endl; return a > b ? a : b; } int Max(int a, int b) { cout << "int Max(int a, int b)" << endl; return a > b ? a : b; } template < typename T > T Max(T a, T b, T c) { cout << "T Max(T a, T b, T c)" << endl; return Max(Max(a, b), c); } int main() { int a = 1; int b = 2; cout << Max(a, b) << endl; // 普通函数 Max(int, int) cout << Max<>(a, b) << endl; // 函数模板 Max<int>(int, int) cout << Max(3.0, 4.0) << endl; // 函数模板 Max<double>(double, double) cout << Max(5.0, 6.0, 7.0) << endl; // 函数模板 Max<double>(double, double, double) cout << Max('a', 100) << endl; // 普通函数 Max(int, int) return 0; }
C++语言中将模板的思想应用于类,使得类的实现不关注数据元素的具体类型,只关注类须要实现的功能。
类模板的定义语法以下:
template <typename T> class classname { };
在类声明前使用template进行标识,<typename T>
用于说明类中使用泛指类型T。
类内定义成员函数
template<typename T> class classname { public: void push(int size) { } }
类外定义函数
template<typename T> void classname<T>::push(T data) { }
类模板实例化为模板类:
classname<double> object;
类模板是类的抽象,类是类模板的实例。
类模板只能显示指定类型参数,没法自动推导。声明的泛型类型参数能够出如今类模板的任意地方。
类模板必须在头文件中实现,不能分开实如今不一样文件中。类模板的成员函数须要定义在外部定义时,每一个成员函数须要加上类模板template<typename T>
声明。
类模板适合以相同的逻辑处理不一样的数据类型的数据,所以很是适合编写数据结构相关代码。
#include <iostream> #include <stdlib.h> #include <stdio.h> #include <string.h> using namespace std; template<typename T> class Stack { public: Stack(int size) { space = new T[size]; top = 0; } ~Stack(); bool isEmpty(); bool isFull(); void push(T data); T pop(); private: T* space; int top; }; template<typename T> Stack<T>::~Stack() { delete []space; } template<typename T> bool Stack<T>::isEmpty() { return top == 0; } template<typename T> bool Stack<T>::isFull() { return top == 1024; } template<typename T> void Stack<T>::push(T data) { space[top++] = data; } template<typename T> T Stack<T>::pop() { return space[--top]; } int main() { Stack<double> s(100); //Stack<string> s(100); if(!s.isFull()) s.push(10.3); if(!s.isFull()) s.push(20); if(!s.isFull()) s.push(30); if(!s.isFull()) s.push(40); if(!s.isFull()) s.push(50); while(!s.isEmpty()) cout<<s.pop()<<endl; return 0; }
类模板经过具体类型产生不一样的类,C++编译器在类模板声明的地方对类模板代码自己进行编译,在使用的地方对类模板参数替换后产生的代码进行编译。
类模板能够定义多个不一样类型参数。
#include <iostream> #include <string> using namespace std; template <typename T> class Operator { public: Operator() { cout << "Operator()" << endl; } T add(T a, T b) { cout << "T add(T a, T b)" << endl; return a + b; } T minus(T a, T b) { return a - b; } T multiply(T a, T b) { return a * b; } T divide(T a, T b) { return a / b; } }; int main(int argc, char *argv[]) { Operator<int> op1; cout << op1.add(1, 2) << endl; cout << op1.add(1, 2) << endl; Operator<string> op2; cout << op2.add("D.T.", "Software") << endl; return 0; } // output: // Operator() // T add(T a, T b) // 3 // Operator() // T add(T a, T b) // 3 // Operator() // T add(T a, T b) // Hello World
上述代码中,类模板中的函数代码在使用的时候才会被分别编译。
类模板能够被特化,如下状况须要特化类模板:
A、指定特定类型的实现
B、部分参数类型必须显示指定
C、根据类型参数分开实现类模板
类模板的特化分为部分特化和彻底特化。部分特化是指用特定规则约束类型参数,彻底特化是指彻底显示指定类型参数。
类模板的特化是模板的分开实现,本质上是同一个类模板,特化类模板必须显示指定每个类型参数。编译器会自动优先选择特化类模板。
#include <iostream> using namespace std; template <typename T1, typename T2> class Test { public: void add(T1 a, T2 b) { cout << "void add(T1 a, T2 b)" << endl; cout << a + b << endl; } }; //部分特化 template <typename T> class Test<T,T> { public: void add(T a, T b) { cout << "void add(T a, T b)" << endl; cout << a + b << endl; } void print() { cout << "class Test <T,T>" << endl; } }; //彻底特化 template <> class Test<int,int> { public: void add(int a, int b) { cout << "void add(int a, int b)" << endl; cout << a + b << endl; } void print() { cout << "class Test<int,int>" << endl; } }; int main(int argc, char *argv[]) { Test<int, int> t1;//彻底特化 t1.add(1,2); t1.print(); Test<double, double> t2;//部分特化 t2.add(3.14,2.0); t2.print(); Test<float, double> t3;//类模板 t3.add(3.14,2.0); return 0; } // output: // void add(int a, int b) // 3 // class Test<int,int> // void add(T a, T b) // 5.14 // class Test <T,T> // void add(T1 a, T2 b) // 5.14
函数模板只支持模板的彻底特化。
#include <iostream> using namespace std; //函数模板 template <typename T> bool Equal(T a, T b) { cout << "bool Equal(T a, T b)" << endl; return a == b; } //函数特化模板 template < > bool Equal<double>(double a, double b) { const double delta = 0.00000000000001; double r = a - b; cout << "bool Equal<double>(double a, double b)" << endl; return (-delta < r) && (r < delta); } //函数重载 bool Equal(double a, double b) { const double delta = 0.00000000000001; double r = a - b; cout << "bool Equal(double a, double b)" << endl; return (-delta < r) && (r < delta); } int main(int argc, char *argv[]) { Equal<double>(0.1,0.1);//函数特化模板 Equal<int>(10,10);//函数模板 Equal(0.1,0.1);//函数重载 return 0; } // output: // bool Equal<double>(double a, double b) // bool Equal(T a, T b) // bool Equal(double a, double b)
工程实践中当须要重载函数模板时,优先使用函数模板特化,当函数模板特化没法知足需求时,使用函数重载。
模板参数能够是数值型参数,数值型模板参数存在限制:
A、变量不能做为模板参数
B、浮点数不能做为模板参数
C、类对象不能做为模板参数
模板参数是在编译阶段处理的,所以在编译阶段须要惟一肯定。
使用最高效方式求1+2+3+4......+100
#include <iostream> using namespace std; template <int N> class Sum { public: static const int value = Sum<N-1>::value + N; }; template <> class Sum<1> { public: static const int value = 1; }; int main(int argc, char *argv[]) { cout<<Sum<100>::value<<endl; return 0; }
#ifndef _ARRAY_H_ #define _ARRAY_H_ template < typename T, int N > class Array { T m_array[N]; public: int length(); bool set(int index, T value); bool get(int index, T& value); T& operator[] (int index); T operator[] (int index) const; virtual ~Array(); }; template < typename T, int N > int Array<T, N>::length() { return N; } template < typename T, int N > bool Array<T, N>::set(int index, T value) { bool ret = (0 <= index) && (index < N); if( ret ) { m_array[index] = value; } return ret; } template < typename T, int N > bool Array<T, N>::get(int index, T& value) { bool ret = (0 <= index) && (index < N); if( ret ) { value = m_array[index]; } return ret; } template < typename T, int N > T& Array<T, N>::operator[] (int index) { return m_array[index]; } template < typename T, int N > T Array<T, N>::operator[] (int index) const { return m_array[index]; } template < typename T, int N > Array<T, N>::~Array() { } #endif
智能指针是C++开发库的重要类模板之一,是自动内存管理的主要手段,能够避开内存的相关问题。
STL中的智能指针分为auto_ptr、shared_ptr、weak_ptr、unique_ptr四类。
auto_ptr智能指针的特性:
A、生命周期结束时,销毁指向的内存空间
B、不能指向堆数组,只能指向堆对象
C、一块堆空间只能属于一个智能指针
D、多个智能指针对象不能指向同一块空间
shared_ptr智能指针的特性:
带有引用计数机制,支持多个指针指向同一对象内存空间。
weak_ptr智能指针的特性:
weak_ptr是一种弱引用,指向shared_ptr所管理的对象。
unique_ptr智能指针的特性:
一个指针对象指向一片内存空间,不能拷贝构造和赋值
STL智能指针使用实例:
#include <iostream> #include <memory> using namespace std; class Test { string m_name; public: Test(const char* name) { cout << "Hello, " << name << "." << endl; m_name = name; } void print() { cout << "I'm " << m_name << "." << endl; } ~Test() { cout << "Goodbye, " << m_name << "." << endl; } }; int main(int argc, char *argv[]) { auto_ptr<Test> pt(new Test("D.T.Software")); cout << "pt = " << pt.get() << endl; pt->print(); cout << endl; auto_ptr<Test> pt1(pt); cout << "pt = " << pt.get() << endl;//NULL cout << "pt1 = " << pt1.get() << endl;// return 0; }
QT中的主要智能指针有:QPointer、QSharedPointer、QWeakPointer、QScopedPointer、QSharedDataPoiner、QExplicitlySharedDataPointer。
QPointer智能指针特性:
A、当其所指向的对象被销毁时会被自动置空
B、析构时不会自动销毁所指向的对象
多个QPointer指针对象能够指向同一内存空间,当所指向对象被销毁时指针会被自动置空,可是指针对象析构时不会自动销毁所指向的对象。
QSharedPointer智能指针特性:
A、引用计数型智能指针
B、能够被自由的拷贝和赋值
C、当引用计数为0时才删除指向的对象
QT中智能指针使用实例:
#include <QPointer> #include <QSharedPointer> #include <QDebug> class Test : public QObject { QString m_name; public: Test(const char* name) { qDebug() << "Hello, " << name << "."; m_name = name; } void print() { qDebug() << "I'm " << m_name << "."; } ~Test() { qDebug() << "Goodbye, " << m_name << "."; } }; int main() { QPointer<Test> pt(new Test("D.T.Software")); QPointer<Test> pt1(pt); QPointer<Test> pt2(pt);//多个QPointer指针对象能够指向同一内存空间 pt->print(); pt1->print(); pt2->print(); delete pt;//QPointer智能指针指向的对象被销毁时,指针对象被置空 qDebug() << "pt = " << pt;//NULL qDebug() << "pt1 = " << pt1;//NULL qDebug() << "pt2 = " << pt2;//NULL qDebug() << endl; QSharedPointer<Test> spt(new Test("Delphi Tang")); QSharedPointer<Test> spt1(spt); QSharedPointer<Test> spt2(spt); spt->print(); spt1->print(); spt2->print(); return 0;//指针对象都被销毁时引用计数为0,自动析构指针指向的对象 }
#ifndef _SMARTPOINTER_H_ #define _SMARTPOINTER_H_ template < typename T > class SmartPointer { T* mp; public: SmartPointer(T* p = NULL) { mp = p; } SmartPointer(const SmartPointer<T>& obj) { mp = obj.mp; const_cast<SmartPointer<T>&>(obj).mp = NULL; } SmartPointer<T>& operator = (const SmartPointer<T>& obj) { if( this != &obj ) { delete mp; mp = obj.mp; const_cast<SmartPointer<T>&>(obj).mp = NULL; } return *this; } T* operator -> () { return mp; } T& operator * () { return *mp; } bool isNull() { return (mp == NULL); } T* get() { return mp; } ~SmartPointer() { delete mp; } }; #endif
某些类在整个系统的生命周期中只能有一个对象存在,即单例模式。
要控制类的对象数目必须隐藏类的构造函数,即构造函数声明为private。
定义一个instance标识符,初始化为NULL,当须要使用对象时查看instance的值,若是instance为NULL则建立对象并用instance标识,若是instance非空则返回instance标识的值。
#ifndef _SINGLETON_H_ #define _SINGLETON_H_ template < typename T > class Singleton { static T* c_instance; public: static T* GetInstance(); }; template < typename T > T* Singleton<T>::c_instance = NULL; template < typename T > T* Singleton<T>::GetInstance() { if( c_instance == NULL ) { c_instance = new T(); } return c_instance; } #endif
使用代码:
#include <iostream> #include <string> #include "Singleton.h" using namespace std; class SObject { friend class Singleton<SObject>; // 当前类须要使用单例模式 SObject(const SObject&); SObject& operator= (const SObject&); SObject() { } public: void print() { cout << "this = " << this << endl; } }; int main() { SObject* s = Singleton<SObject>::GetInstance(); SObject* s1 = Singleton<SObject>::GetInstance(); SObject* s2 = Singleton<SObject>::GetInstance(); s->print(); s1->print(); s2->print(); return 0; }
判断一个变量是否是指针
C++编译器匹配的调用优先级:
A、重载函数
B、函数模板
C、变参函数
能够根据C++编译器匹配的调用优先级,将函数模板匹配指针变量,返回true,变参函数匹配非指针变量,返回false。
template <typename T> bool IsPtr(T *pt) { return true; } bool IsPtr(...) { return false; }
可是,因为变参函数是C语言的内容,没法解析C++自定义类型对象,可能形成程序崩溃。
template <typename T> char IsPtr(T* v) // match pointer { return 'd'; } int IsPtr(...) // match non-pointer { return 0; } #define ISPTR(p) (sizeof(IsPtr(p)) == sizeof(char))
上述代码中,C++编译器在编译时会进行函数匹配,不会进行调用,避免了参数为自定义对象时调用变参函数致使的程序崩溃。
C++语言在引入了面向对象编程思想时,使用class关键字定义类类型。C++语言发展过程当中引入了泛型编程,直接复用class关键字来定义模板。但泛型编程针对的不仅是类类型,直接复用class关键字会使代码出现二义性。所以,C++直接引入了typename关键字,用于在模板定义中声明泛指类型,明确告诉C++编译器声明的标识符为类型。
C++语言中容许类定义中嵌套类型,所以当自定义类类型中嵌套类型的标识符与其它类类型中定义的成员变量标识符重名时将会形成二义性。不一样类中的同名标识符表明可能致使二义性,所以C++编译器没法识别标识符的确切意义。
#include <iostream> using namespace std; class Test1 { public: static const int NS = 1; }; class Test2 { public: struct NS { int value; }; }; int a = 0; template <class T> void func() { T::NS* a; } int main(int argc, char *argv[]) { func<Test1>(); //func<Test2>();//error //error: dependent-name 'T:: NS' is parsed as a non-type, //but instantiation yields a type //say 'typename T:: NS' if a type is meant return 0; }
上述代码中,C++编译器不会将func函数模板中NS解析为类型,所以使用Test2做为参数时,C++编译器会报错。所以,为了将NS明确声明为类型,须要使用typename关键字对NS标识符进行声明。代码以下:
#include <iostream> using namespace std; class Test1 { public: static const int NS = 1; }; class Test2 { public: struct NS { int value; }; }; int a = 0; template <class T> void func() { typename T::NS* a; } int main(int argc, char *argv[]) { //func<Test1>();//error func<Test2>(); return 0; }
上述代码中,NS被明确声明为类型,所以若是使用Test1做为参数,func函数模板将会报错。