【C++】模板

这里写目录标题

  • 前言
  • 模板
  • 函数模板的使用
  • 重载函数模板
  • 类模板
  • 使用类模板
  • 非类型模板参数
  • 缺省模板参数
  • 成员模板
  • 关键字typename
  • 派生类和模板
  • 面向对象与泛型编程
  • 用模板实现单例模式
  • 用模板方式实现动态创建对象

前言

考虑求两数较大值函数max(a,b),对于a,b的不同类型,都有相同的处理形式:

return a<b?b:a;

用已有的方法解决:
(1)宏替换

#define max(a,b)((a)<(b)?(b):(a))

缺点是避开了类型检查。
(2)重载
缺点是需要许多重载版本。
(3)使用函数模板
为相同逻辑功能提供一个模板,将类型当作参数来传递,让编译器实例化对应版本的函数来处理。

模板

模板是一种参数化的多态工具。所谓参数化的多态性,是指将程序所处理的对象的类型参数化,使一段程序代码可以用于处理多种不同类型的对象。采用模板编程,可以为各种逻辑功能相同而数据类型不同的程序提供一种代码共享的机制。

函数模板的使用

模板被编译了两次,

  • 实例化之前,先检查模板代码本身,查看语法是否正确;在这里会发现语法错误,如果遗漏分号等。
  • 实例化期间,检查模板代码。查看是否所有的调用都有效。在这里会发现无效的调用,如该实例化类型不支持某些函数调用等。

普通函数只需要声明,即可顺利编译,而模板的编译需要查看模板的定义。

重载函数模板

C++语言可以重载一个函数模板,用户可以用非模板函数重载一个同名的函数模板。
max.h:

#pragma once
#include <iostream>
using namespace std;template<typename T>
const T& max(const T& a, const T& b)
{return a < b ? b : a;
}//函数模板重载
template<typename T>
const T& max(const T& a, const T& b, const T& c)
{return ::max(a, b) < c ? c : ::max(a, b);
}//非模板函数重载
const int& max(const int& a, const int& b)
{cout << "non template function" << endl;return a < b ? b : a;
}//函数模板特化
template<>
const char* const& max(const char* const& a, const char* const& b)
{return strcmp(a, b) < 0 ? b : a;
}

main.cpp:

#include <iostream>
#include "max.h"
using namespace std;class Test
{
public:friend bool operator<(const Test& t1, const Test& t2){return true;}
};int main()
{cout << ::max(5.5, 6.6) << endl;        //自动推导  max(const int&, const int&)cout << ::max('A', 'C') << endl;Test t1, t2;::max(t1, t2);const char* str1 = "aaa";const char* str2 = "zzz";cout << ::max(str1, str2) << endl;cout << ::max(1, 5, 3) << endl;cout << ::max('A', 100) << endl;cout << ::max(97, 100) << endl;         //max(const int&, const int&);cout << ::max<>(97, 100) << endl;       //自动推导  max(const int&, const int&)cout << ::max<int>(97, 100) << endl;    //显示调用模板函数 max(const int&, const int&)cout << ::max<int>('A', 100) << endl;return 0;
}

类模板

将类定义中的数据类型参数化。类模板实际上是函数模板的推广,可以用相同的类模板来组建任意类型的对象集合。

使用类模板

类模板的实例化:用具体的数据类型替换模板的参数以得到具体的类(模板类)。模板类也可以实例化对象。

Stack.h:

#pragma once
#include <exception>
#include <iostream>
using namespace std;template <typename T>
class Stack
{
public:explicit Stack(int maxSize);~Stack();void Push(const T& elem);void Pop();T& Top();const T& Top() const;bool Empty() const;
private:T* elem_;int maxSize_;int top_;
};template <typename T>
Stack<T>::Stack(int maxSize) :maxSize_(maxSize), top_(-1)
{elem_ = new T[maxSize_];
}template <typename T>
Stack<T>::~Stack()
{delete[] elem_;
}template <typename T>
void Stack<T>::Push(const T& elem)
{if (top_ + 1 == maxSize_)throw out_of_range("Stack<>::Push() stack full");elem_[++top_] = elem;
}template <typename T>
void Stack<T>::Pop()
{if (top_ + 1 == 0)throw out_of_range("Stack<>::Push() stack empty");--top_;
}template<typename T>
T& Stack<T>::Top()
{if (top_ + 1 == 0)throw out_of_range("Stack<>::Push() stack empty");return elem_[top_];
}template<typename T>
const T& Stack<T>::Top() const
{if (top_ + 1 == 0)throw out_of_range("Stack<>::Push() stack empty");return elem_[top_];
}template<typename T>
bool Stack<T>::Empty() const
{return top_ + 1 == 0;
}

main.cpp:

#include <iostream>
#include "Stack.h"
using namespace std;int main()
{Stack<int> s(10);s.Push(1);s.Push(2);s.Push(3);while (!s.Empty()){cout << s.Top() << endl;s.Pop();}return 0;
}

非类型模板参数

对于函数模板与类模板,模板参数并不局限于类型,普通值也可以作为模板参数。

Stack2.h:

#pragma once
#include <exception>
#include <iostream>
using namespace std;template <typename T, int MAX_SIZE>
class Stack2
{
public:Stack2();~Stack2();void Push(const T& elem);void Pop();T& Top();const T& Top() const;bool Empty() const;
private:T* elem_;int maxSize_;int top_;
};template <typename T, int MAX_SIZE>
Stack2<T, MAX_SIZE>::Stack2() :top_(-1)
{elem_ = new T[MAX_SIZE];
}template <typename T, int MAX_SIZE>
Stack2<T, MAX_SIZE>::~Stack2()
{delete[] elem_;
}template <typename T, int MAX_SIZE>
void Stack2<T, MAX_SIZE>::Push(const T& elem)
{if (top_ + 1 == MAX_SIZE)throw out_of_range("Stack<>::Push() stack full");elem_[++top_] = elem;
}template <typename T, int MAX_SIZE>
void Stack2<T, MAX_SIZE>::Pop()
{if (top_ + 1 == 0)throw out_of_range("Stack<>::Push() stack empty");--top_;
}template <typename T, int MAX_SIZE>
T& Stack2<T, MAX_SIZE>::Top()
{if (top_ + 1 == 0)throw out_of_range("Stack<>::Push() stack empty");return elem_[top_];
}template <typename T, int MAX_SIZE>
const T& Stack2<T, MAX_SIZE>::Top() const
{if (top_ + 1 == 0)throw out_of_range("Stack<>::Push() stack empty");return elem_[top_];
}template <typename T, int MAX_SIZE>
bool Stack2<T, MAX_SIZE>::Empty() const
{return top_ + 1 == 0;
}

main.cpp:

#include <iostream>
#include "Stack2.h"
using namespace std;int main()
{Stack2<int, 10> s;s.Push(1);s.Push(2);s.Push(3);while (!s.Empty()){cout << s.Top() << endl;s.Pop();}return 0;
}

缺省模板参数

Stack.h:

#pragma once
#include <exception>
#include <iostream>
#include <deque>
using namespace std;template <typename T, typename CONT = deque<T> >
class Stack
{
public:Stack() :c_(){}~Stack(){}void Push(const T& elem){c_.push_back(elem);}void Pop(){c_.pop_back();}T& Top(){return c_.back();}const T& Top() const{return c_.back();;}bool Empty() const{return c_.empty();}
private:CONT c_;int top_;
};

main.cpp:

#include <iostream>
#include "Stack.h"
#include <vector>
using namespace std;int main()
{Stack<int> s;//Stack<int,vector<int>> s;s.Push(1);s.Push(2);s.Push(3);while (!s.Empty()){cout << s.Top() << endl;s.Pop();}return 0;
}

成员模板

#include <iostream>
using namespace std;template <typename T>
class MyClass
{
private:T value;
public:MyClass(){}template <class X>MyClass(const MyClass<X>& x) :value(x.GetValue()){}template <class X>void Assign(const MyClass<X>& x){value = x.GetValue();}T GetValue() const { return value; }
};
int main()
{MyClass<double> d;MyClass<int> i; d.Assign(d);d.Assign(i);MyClass<double> d2(i);return 0;
}  

关键字typename

typename表示类型,而不是引用类型的静态成员变量。

#include <iostream>
using namespace std;template <typename T>
class MyClass
{
private:typename T::SubType* ptr;
public:};class Test
{
public:typedef int SubType;
};
int main()
{MyClass<Test> t;return 0;
}  

派生类和模板

为了运行的效率,类模板是相互独立的,即独立设计,没有使用继承的思想。对类模板的扩展是采用适配器(adapter)来完成的。通用性是模板库的设计出发点之一,这是由泛型算法和函数对象等手段达到的。派生的目标之一也是代码的复用和程序的通用性,最典型的就是MFC,派生类的优点是可以由简到繁,逐步深入,程序编制过程中可以充分利用前面的工作,一步步完成一个复杂的任务。模板追求的是运行效率,而派生追求的是编程的效率。

面向对象与泛型编程

面向对象与泛型都依赖于某个形式的多态。面向对象包括虚函数的动态多态和模板的静态多态。面向对象中的多态在运行时应用存在继承关系。我们编写使用这些类的代码,忽略基类与派生类之间的类型差异。只要使用基类指针或者引用,基类类型对象、派生类类型对象就可以共享相同的代码。在泛型编程中,我们所编写的类和函数能够多态的用于编译时不相关的类型。一个类或一个函数可以用来操纵多种类型的对象。

用模板实现单例模式

Singleton.h:

#pragma once
#include <iostream>
#include <cstdlib>
using namespace std;template <typename T>
class Singleton
{
public:static T& GetInstance(){Init();return *instance_;}private:static void Init() {if (instance_ == 0){instance_ = new T;atexit(Destroy);}}static void Destroy(){delete instance_;}Singleton(const Singleton& other);Singleton& operator=(const Singleton& other);Singleton();~Singleton();static T* instance_;
};template <typename T>
T* Singleton<T>::instance_ = 0;

main.cpp:

#include <iostream>
#include "Singleton.h"
using namespace std;class ApplicationImpl
{
public:ApplicationImpl() { cout << "ApplicationImpl ..." << endl; }~ApplicationImpl() { cout << "~ApplicationImpl ..." << endl; }void run() { cout << "run ..." << endl; }
};typedef Singleton<ApplicationImpl> Application;int main()
{Application::GetInstance().run();Application::GetInstance().run();return 0;
}

用模板方式实现动态创建对象

Shape.h:

#pragma once
#include <iostream>
#include <vector>
using namespace std;
class Shape
{
public:virtual void Draw() = 0;virtual ~Shape() {};
};class Circle :public Shape
{
public:void Draw();~Circle();
};class Square :public Shape
{
public:void Draw();~Square();
};class Rectangle :public Shape
{
public:void Draw();~Rectangle();
};

Shape.cpp:

#include "Shape.h"
#include <iostream>
#include <vector>
#include "DynBase.h"
using namespace std;void Circle::Draw()
{cout << "Circle::Draw() ..." << endl;
}
Circle::~Circle()
{cout << "~Circle ..." << endl;
}void Square::Draw()
{cout << "Square::Draw() ..." << endl;
}
Square::~Square()
{cout << "~Square ..." << endl;
}void Rectangle::Draw()
{cout << "Rectangle::Draw() ..." << endl;
}
Rectangle::~Rectangle()
{cout << "~Rectangle ..." << endl;
}REGISTER_CLASS(Circle);
REGISTER_CLASS(Square);
REGISTER_CLASS(Rectangle);/*宏展开
class CircleRegister
{
public:static void* NewInstance(){return new Circle;}
private:static Register reg_;//声明
}
Register CircleRegister::reg_("Circle",CircleRegister::NewInstance)
*//*宏#define REGISTER_CLASS(class_name) \
class class_name##Register \
{ \
public: \static void* NewInstance() \{ \return new class_name; \} \
private: \static Register reg_; \
}; \
Register class_name##Register::reg_(#class_name,class_name##Register::NewInstance)*/

DynBase.h:

#pragma once
#include <iostream>
#include <string>
#include <map>
using namespace std;typedef void* (*CREATE_FUNC)();class DynObjectFactory
{
public:static void* CreateObject(const string& name){map<string, CREATE_FUNC>::const_iterator it;it = mapCls_.find(name);if (it == mapCls_.end())return 0;elsereturn it->second();}static void  Register(const string& name, CREATE_FUNC func){mapCls_[name] = func;}
private:static map<string, CREATE_FUNC> mapCls_;
};//g++
//__attribute((weak))
_declspec(selectany) map<string, CREATE_FUNC> DynObjectFactory::mapCls_;class Register
{
public:Register(const string& name, CREATE_FUNC func){DynObjectFactory::Register(name, func);}
};//#define REGISTER_CLASS(class_name) \
//class class_name##Register \
//{ \
//public: \
//	static void* NewInstance() \
//	{ \
//		return new class_name; \
//	} \
//private: \
//	static Register reg_; \
//}; \
//Register class_name##Register::reg_(#class_name,class_name##Register::NewInstance)template <typename T>
class DelegatingClass
{
public:DelegatingClass(const string& name){DynObjectFactory::Register(name, &(DelegatingClass::NewInstance));}static void* NewInstance(){return new T;}
};#define REGISTER_CLASS(class_name) DelegatingClass<class_name> class##class_name(#class_name)

DynBase.cpp:

#include "DynBase.h"
#include <iostream>
#include <vector>
#include "Shape.h"
using namespace std;void DrawAllShapes(const vector<Shape*>& v)
{vector<Shape*>::const_iterator it;for (it = v.begin(); it != v.end(); ++it){(*it)->Draw();}
}void DeleteAllShapes(const vector<Shape*>& v)
{vector<Shape*>::const_iterator it;for (it = v.begin(); it != v.end(); ++it){delete (*it);}
}int main()
{vector<Shape*> v;Shape* ps;ps = static_cast<Shape*>(DynObjectFactory::CreateObject("Circle"));v.push_back(ps);ps = static_cast<Shape*>(DynObjectFactory::CreateObject("Square"));v.push_back(ps);ps = static_cast<Shape*>(DynObjectFactory::CreateObject("Rectangle"));v.push_back(ps);DrawAllShapes(v);DeleteAllShapes(v);return 0;
}

本文链接:https://my.lmcjl.com/post/13679.html

展开阅读全文

4 评论

留下您的评论.