把梦想实现为现实
我们要实现的梦想:
template< typename Policy1 = DefaultPolicy1,
typename Policy2 = DefaultPolicy2,
typename Policy3 = DefaultPolicy3,
typename Policy4 = DefaultPolicy4,
typename Policy5 = DefaultPolicy5
>
class X;
对于上面这个类模板,如果我们只想指定模板参数Policy3,在梦想实现以前你必须这样做:
X<DefaultPolicy1, DefaultPolicy2, MyPolicy3> x;
相信你已经发现问题了,上述如果我们想指定自己的MyPolicy3模参,那么前面的Policy1、Policy2必须指定,这得有多麻烦和不优雅,因此我们的梦想就是这样:
X<Policy3=MyPolicy3> x;
准备实现:
思路:将缺省类型值放到一个基类中,然后通过派生类覆盖掉某些类型值!
源码:
#include<iostream>
using namespace std;
//四个默认的Policy策略类
class DefaultPolicy1{
public:
void print(){
cout << "class DefaultPolicy1" << endl;
}
};
class DefaultPolicy2{
public:
void print(){
cout << "class DefaultPolicy2" << endl;
}
};
class DefaultPolicy3{
public:
void print(){
cout << "class DefaultPolicy3" << endl;
}
};
class DefaultPolicy4{
public:
void print(){
cout << "class DefaultPolicy4" << endl;
}
};
//默认的,其实当PolicySelector必须继承DefaultPolicies的时候DefaultPolicyArgs就没用了!
class DefaultPolicyArgs{
public:
void print(){
cout << "class DefaultPolicyArgs" << endl;
}
};
//用来被覆写的基类
class DefaultPolicies{
public:
typedef DefaultPolicy1 P1;
typedef DefaultPolicy2 P2;
typedef DefaultPolicy3 P3;
typedef DefaultPolicy4 P4;
};
//当用户使用所有的默认模板的时候,保证Policies::P3().print()中的P3有值!
//无论如何都保证PolicySelector的继承体系中有DefaultPolicies类
class PolicyDefault_is :virtual public DefaultPolicies{
};
//将传递进来的Policy替换DefaultPolicies基类中的Pn
template<typename Policy>
class Policy1_is :virtual public DefaultPolicies{
public:
typedef Policy P1;
};
template<typename Policy>
class Policy2_is :virtual public DefaultPolicies{
public:
typedef Policy P2;
};
template<typename Policy>
class Policy3_is :virtual public DefaultPolicies{
public:
typedef Policy P3;
};
template<typename Policy>
class Policy4_is :virtual public DefaultPolicies{
public:
typedef Policy P4;
};
//Discriminator:"鉴别器"——避免二义性
template<typename Base, int D>
class Discriminator :public Base{
};
//PolicySelector
template<
typename Setter1,
typename Setter2,
typename Setter3,
typename Setter4
>
class PolicySelector :public Discriminator<Setter1, 1>,
public Discriminator<Setter2, 2>,
public Discriminator<Setter3, 3>,
public Discriminator<Setter4, 4>,
public Discriminator<PolicyDefault_is, 5>{
};
//我自己的Policy测试类
class MyPolicy1{
public:
void print(){
cout << "class MyPolicy1" << endl;
}
};
class MyPolicy2{
public:
void print(){
cout << "class MyPolicy2" << endl;
}
};
class MyPolicy3{
public:
void print(){
cout << "class MyPolicy3" << endl;
}
};
class MyPolicy4{
public:
void print(){
cout << "class MyPolicy4" << endl;
}
};
//BreadSlicer
template<
typename PolicySetter1 = DefaultPolicyArgs,
typename PolicySetter2 = DefaultPolicyArgs,
typename PolicySetter3 = DefaultPolicyArgs,
typename PolicySetter4 = DefaultPolicyArgs
>
class BreadSlicer{
private:
//PolicySelector:"策略选择器"
typedef PolicySelector<
PolicySetter1,
PolicySetter2,
PolicySetter3,
PolicySetter4
> Policies;
public:
void print_Policy1(){
Policies::P1().print();
}
void print_Policy2(){
Policies::P2().print();
}
void print_Policy3(){
Policies::P3().print();
}
void print_Policy4(){
Policies::P4().print();
}
};
void main(){
cout << "验证普通功能:" << endl;
BreadSlicer<Policy4_is<MyPolicy4>,Policy1_is<MyPolicy1>> breadslicer;
breadslicer.print_Policy1();
breadslicer.print_Policy2();
breadslicer.print_Policy3();
breadslicer.print_Policy4();
cout << "------------------------------------" << endl;
cout << "验证重新指定也能工作:" << endl;
BreadSlicer<Policy4_is<MyPolicy4>, Policy1_is<MyPolicy1>, Policy4_is<MyPolicy4>/*重新指定也没关系*/> breadslicer2;
breadslicer2.print_Policy1();
breadslicer2.print_Policy2();
breadslicer2.print_Policy3();
breadslicer2.print_Policy4();
cout << "------------------------------------" << endl;
cout << "验证PolicySelector必须继承一次DefaultPolicies必要条件:" << endl;
//如果没有PolicySelector必须继承一次DefaultPolicies这个条件,那么默认调用将出错:
//错误 1 error C2039: “P1”: 不是“PolicySelector<PolicySetter1,PolicySetter2,PolicySetter3,PolicySetter4>”的成员
BreadSlicer<> breadslicer3;
breadslicer3.print_Policy1();
breadslicer3.print_Policy2();
breadslicer3.print_Policy3();
breadslicer3.print_Policy4();
/*
输出结果:
验证普通功能:
class MyPolicy1
class DefaultPolicy2
class DefaultPolicy3
class MyPolicy4
------------------------------------
验证重新指定也能工作:
class MyPolicy1
class DefaultPolicy2
class DefaultPolicy3
class MyPolicy4
------------------------------------
验证PolicySelector必须继承一次DefaultPolicies必要条件:
class DefaultPolicy1
class DefaultPolicy2
class DefaultPolicy3
class DefaultPolicy4
*/
}
画图分析模型:
这个模型有两点需要关注的:
1)不论是默认的Policy类型,但是后面由用户指定的Policy类型,都必须是类类型(或类类类型),因为Discriminator那一层将继承这些所有的Policy类型,而int显然不能被继承!但是也不是不可以(由于我们的分析,我们依然可以将基本类型等不适类类型的类型打包成类类型来完成这个模型,只是BreadSlicer的内部设计要设计得当!)
2)C++Template书中的此例并没有将“PolicySelector必须继承一次DefaultPolicies”考虑其中(此例中我将其补充了!),如果没有这个条件,那么BreadSlicer中“Policies::P1().print();“的P1将无从找起!
现在让我们来分析分析这个模型中运用到的技术:
1:
//Discriminator:"鉴别器"——避免二义性
template<typename Base, int D>
class Discriminator :public Base{
};
上面代码中的技术在于:这是一个参数化继承,即Discriminator将继承模板参数,而这也隐式限定了Base必须是类类类型的模参!
2:
//PolicySelector
template<
typename Setter1,
typename Setter2,
typename Setter3,
typename Setter4
>
class PolicySelector :public Discriminator<Setter1, 1>,
public Discriminator<Setter2, 2>,
public Discriminator<Setter3, 3>,
public Discriminator<Setter4, 4>,
public Discriminator<PolicyDefault_is, 5>{
};
上面代码中的技术在于:使用虚继承,使得虚继承于基类的所有子类有共同的权利和机会修改基类的数据:
上述代码的另一个技术在于:利用类模板创建不同的类型,避免多继承于相同的类而导致的二义性;
空基类优化
空类:(一般而言)只包含类型成员、非虚成员函数、静态数据成员的类,而非静态数据成员、虚成员函数、虚基类则的确在运行期耗费内存!
伪空类:
#include<iostream>
using namespace std;
class X{};
void main(){
cout << "sizeof:class X{}; " << sizeof(X) << endl;
/*
输出结果:
sizeof:class X{}; 1
*/
}
X类看似什么都不含,但是还是有1个字节的内存开销(而在有些对对齐要求更严格的系统上,X的大小可能是4)
类大小不能是0的一种原因:数组操作会失效
#include<iostream>
using namespace std;
class X{};
void main(){
X arr[10];
auto temp=&arr[5] - &arr[2];
cout << temp << endl;
/*
输出结果:
3
*/
}
上面的实例我们计算两个元素之间的距离,这个距离的计算是通过区间间的字节数除以内存大小来得出的(例如:1*3/1=3),但是如果X内存大小为0,除以0/0将导致错误!
然而C++标准规定:当空类作为基类时,只要不会与同一类型的另一个对象或者子对象分配在同一地址,就不需为其分配任何空间(简称EBCO原则:empty base class optimization)
示例:
#include<iostream>
using namespace std;
class X{};
class C :public X{
public:
void PrintBaseSize(){
cout << "sizeof(X):" << sizeof(X)<<endl;
}
};
class D :public C{};//C带有一个优化空基类,所以当C单独存在时,其大小是1,但是当C作为基类时,其大小归为0
void main(){
cout << "sizeof(C):" << sizeof(C)<< endl;
cout << "sizeof(D):" << sizeof(D) << endl;
cout << "----------------------------" << endl;
cout << "PrintBaseSize:" << endl;
C().PrintBaseSize();
/*
输出结果“
sizeof(C):1
sizeof(D):1
----------------------------
PrintBaseSize:
sizeof(X):1
*/
}
从这个示例中可以看出:
1)C带有一个优化空基类,当C单独存在时,其大小为非0,但是当C作为基类时,其大小归为0
2)从类的内部测试空基类的大小为非0
内存模型分析(上例):
可以看出,如果没有运用EBCO原则,最终D将被分配3字节的内存空间
一个不会运用EBCO原则的例子:
#include<iostream>
using namespace std;
class X{
typedef int a;
};
class C :public X{};
class D :public X,public C{};
//warning C4584 : “D” : 基类“X”已是“C”的基类
void main(){
cout << "sizeof(C):" << sizeof(C)<< endl;
cout << "sizeof(D):" << sizeof(D) << endl;
}
此例中:D中有两个相同的子对象(D的直接基类X、C的直接基类X),这违反了“在两个相同的子对象不会被分配到同一地址空间的前提下才会运用EBCO”的原则,于是将对其中之一X不使用EBCO原则,那么其内存布局将是:
(此例中只是给出一个警告,并没有得出sizeof(D)=2的结果)
对EBCO进行限制的根本原因在于,当用指针判断指向不同的对象的时候,EBCO原则将导致问题,因为指针一般是以对象内存地址表示的,而如果两个相同的子对象共用一块内存地址,那么将不能用指针区分他们!
模板遇见EBCO:
一个貌似可以对模板参数实现EBCO的例子:
#include<iostream>
using namespace std;
template<typename T1,typename T2>
class X{
private:
T1 t1;
T2 t2;
};
void main(){
}
上例中,如果T1和T2是两个空类类型,那么t1、t2将无故消耗两个字节的内存(例如T1、T2是内部包含typedef群的类)
利用EBCO的原则,我们可能想这样解决这个问题:
#include<iostream>
using namespace std;
template<typename T1,typename T2>
class X:private T1,private T2 {
private:
};
void main(){
}
利用EBCO的东风,我们想将T1和T2继承以避免开销,但是这样做有很多潜在问题:
1)如果T1、T2并非是类
2)如果T1、T2是相同的类类型
3)增加基类会改变接口,如果T1(或者T2)中有一个虚函数FUN,而在X类中也有一个非虚函数FUN,那么X中非虚函数FUN将被改写为virtual FUN
——因此,这样引入EBCO将带来很多不必要的麻烦:
一个可以利用EBCO的模板例子(如果已知一个模板参数必然是类,模板的另一个数据成员不是空类,就像下面的这种情况:)
#include<iostream>
using namespace std;
template<typename BaseClass>
class Optimizabale{
private:
BaseClass info;
void * member;
};
void main(){
}
接下来我们实现怎样对info优化(member是数据成员,不可能被优化的):
BaseMemberPair头文件:
#ifndef BSAEMEMBERPAIR
#define BSAEMEMBERPAIR
template<typename Base/*空基类的类型*/,typename Member/*数据成员的类型*/>
class BaseMemberPair :private Base{
private:
Member member;
public:
BaseMemberPair(Base const & b, Member const & m):Base(b),member(m){
}
//返回空类
Base const &first()const{
return (Base const &)*this;
}
Base &first(){
return (Base &)*this;
}
//返回数据成员
Member const & second()const{
return this->member;
}
Member & second(){
return this->member;
}
};
#endif
测试源文件:
#include<iostream>
#include"BaseMemberPair.h"
using namespace std;
//template<typename BaseClass>
//class Optimizabale{
//private:
// BaseClass info;
// void * member;
//};
template<typename EmptyClass>
class Optimizabale{
private:
BaseMemberPair<EmptyClass, void *> info_and_member;
public:
void FUN(){
//访问EmptyClass和数据成员
info_and_member.first;
info_and_member.second;
}
};
void main(){
}
其实上面例子或许展示的是一种策略,但是不见得高明多少,如果在已知模板参数是类的情况下,此时可以直接Optimizabale来继承模板参数,而不需要弄一个中间层BaseMemberPair:(或许上述例子能使代码优雅也未可知!)
template<typename EmptyClass>
class Optimizabale:private EmptyClass{
private:
void * member;
public:
void FUN(){
}
};
CRTP:Curiously Recurring Template Patteern(奇特的递归模板模式)
什么是CRTP:派生类将自身当做模板参数传递给基类
例如:
#include<iostream>
using namespace std;
//第一种形式
template<typename T>
class CuriousBase{};
template<typename T>
class CuriousTemplate :public CuriousBase<CuriousTemplate<T>>{};
//第二种形式
template<template<typename> class T>
class MoreCuriousBase{};
template<typename T>
class MoreCuriousTemplate :public MoreCuriousBase<MoreCuriousTemplate>{};
void main(){
}
其中第一种形式和第二种形式都是CRTP
CRTP运用的例子:记录某个类的对象构造的总个数
普通的实现:
#include<iostream>
#include<stddef.h>
using namespace std;
template<typename CharT>
class MyString{
private:
static size_t count;
public:
MyString(){
++count;
}
//拷贝构造
MyString(MyString<CharT> const &){
++count;
}
//析构
~MyString(){
--count;
}
static size_t live(){
return count;
}
};
//类外定义初始化
template<typename CounterType>
size_t MyString<CounterType>::count = 0;
void main(){
MyString<char> s1, s2, s3, s4(s1);
MyString<wchar_t> ws1,ws2,ws3,ws4(ws2);
auto Mp = new MyString<char>();
cout << "number of MyString<char>:" << MyString<char>::live() << endl;
cout << "number of MyString<wchar_t>:" << MyString<wchar_t>::live() << endl;
cout << "------------------------" << endl;
delete Mp;
cout << "number of MyString<char>:" << MyString<char>::live() << endl;
cout << "number of MyString<wchar_t>:" << MyString<wchar_t>::live() << endl;
/*
输出结果:
number of MyString<char>:5
number of MyString<wchar_t>:4
------------------------
number of MyString<char>:4
number of MyString<wchar_t>:4
*/
}
普通的实现缺点:要类自身管理count(即MyString自身),需写出在构造、析构、拷贝构造中管理count的代码!
CRTP的实现:
objectcounter头文件:
#ifndef OBJECTCOUNTER
#define OBJECTCOUNTER
#include<stddef.h>
template<typename CounterType>
class ObjectCounter{
private:
static size_t count;
protected:
//缺省构造
ObjectCounter(){
++count;
}
//拷贝构造
ObjectCounter(ObjectCounter<CounterType> const &){
++count;
}
//析构
~ObjectCounter(){
--count;
}
public:
static size_t live(){
return count;
}
};
//类外定义初始化
template<typename CounterType>
size_t ObjectCounter<CounterType>::count = 0;
#endif
测试源文件:
#include<iostream>
#include"objectcounter.h"
#include<stddef.h>
using namespace std;
template<typename CharT>
class MyString:public ObjectCounter<MyString<CharT>>{
};
void main(){
MyString<char> s1, s2, s3, s4(s1);
MyString<wchar_t> ws1,ws2,ws3,ws4(ws2);
auto Mp = new MyString<char>();
cout << "number of MyString<char>:" << MyString<char>::live() << endl;
cout << "number of MyString<wchar_t>:" << MyString<wchar_t>::live() << endl;
cout << "------------------------" << endl;
delete Mp;
cout << "number of MyString<char>:" << MyString<char>::live() << endl;
cout << "number of MyString<wchar_t>:" << MyString<wchar_t>::live() << endl;
/*
输出结果:
number of MyString<char>:5
number of MyString<wchar_t>:4
------------------------
number of MyString<char>:4
number of MyString<wchar_t>:4
*/
}
分析:
每个自己写的类都继承一个类模板ObjectCounter,这个类模板管理着一个泛型的引用计数count,ObjectCounter每被实例化一次,就分配一个count给我们自己写的类(已被实例化),其中最大的好处在于取代了我们自己写的类来管理引用计数的工作!
巧妙之处分析:
template<typename CharT>
class MyString:public ObjectCounter<MyString<CharT>>{
};
//其中ObjectCounter<MyString<CharT>>中的MyString<CharT>只是充当一个类型参数,就只是巧妙地化解了实例化ObjectCounter的时候需要指定类型参数——这就是运用CRTP
参数化虚拟性——双刃剑
概念:当一个类被作为模板参数传递,并且使用该模板参数的类模板继承了这个模板参数,那么,当这个被作为模板参数传递的类的内部如果有虚函数(例如virtual FUN),此时使用该模板参数的类模板内部的FUN函数也会自动变为virtual FUN(不管原来是不是virtual FUN),概念总是绕口的,让我们来看图说话:
#include<iostream>
using namespace std;
class Virtual{
public:
virtual void FUN(){
cout << "class Vitrual" << endl;
}
};
template<typename VBase>
class Base:public VBase{
public:
void FUN(){
cout << "class Base:public VBase" << endl;
}
};
template<typename V>
class Drived :public Base<V>{
public:
void FUN(){
cout << "class Drived :public Base<V>" << endl;
}
};
void main(){
Virtual * p1 = new Base<Virtual>;
p1->FUN();
Virtual * p2 = new Drived<Virtual>;
p2->FUN();
/*
输出结果:
class Base:public VBase
class Drived :public Base<V>
*/
}
看图分析:
参数化虚拟性使得一个类模板身兼两值:既可以用作实例化,也可以用作继承:
Virtual * p1 = new Base<Virtual>;
p1->FUN();
Virtual * p2 = new Drived<Virtual>;
p2->FUN();
本来Virtual类和Base类本来只是继承的关系,但是此时Virtual和Base之间产生了多态,因此需要留神P2->FUN()调用的其实是Base的FUN!