Day48、模板特化、智能指针、模板的其他特性

来源:互联网 发布:九三学社入社条件知乎 编辑:程序博客网 时间:2024/05/14 04:13

7、特(例)化

当一个类模板的通用实现无法满足某些特殊类型的需要,或者虽然可以满足其需要,但是性能不佳,这时可以编写针对该特殊类型的特殊实现,这就叫做类模板的特(例)化。

1) 完全特化:针对全部类型参数的特化。

A:全类特化:用特定类型替换类型参数,把整个类模板重写一遍

B:成员特化:只重写类模板中部分与特定类型相关的成员函数

#include <iostream>

#include <cstring>

using namespace std;

// 通用版本

template<typename T>

T max (T x, T y) {

       returnx < y ? y : x;

}

// 针对字符指针类型的重载版本

char* max (char* x, char* y) {

       returnstrcmp (x, y) < 0 ? y : x;

}

// 通用版本

template<typename T>

class Comparator {

public:

       Comparator(T x, T y) :

              m_x(x), m_y (y) {}

       Tmax (void) const {

              returnm_x < m_y ? m_y : m_x;

       }

       /*

       char*max (void) const {

              returnstrcmp (m_x, m_y) < 0 ?

                     m_y: m_x;

       }

       */

private:

       Tm_x, m_y;

};

// 针对字符指针类型的特化版本

/*

template<>

class Comparator<char*> {

public:

       Comparator(char* x, char* y) :

              m_x(x), m_y (y) {}

       char*max (void) const {

              returnstrcmp (m_x, m_y) < 0 ?

                     m_y: m_x;

       }

private:

       char*m_x, *m_y;

};

*/

template<>

char* Comparator<char*>::max (void)const {

       returnstrcmp (m_x, m_y) < 0 ? m_y : m_x;

}

int main (void) {

       inta = 123, b = 456;

       cout<< ::max (a, b) << endl;

       charc[] = "hello", d[] = "world";

       cout<< ::max (c, d) << endl;

       /*

       cout<< ::max<string> (c, d) << endl;

       cout<< ::max (string (c), string (d))

              <<endl;

       */

       Comparator<int>ci (a, b);

       cout<< ci.max () << endl;

       Comparator<char*>cs (c, d);

       cout<< cs.max () << endl;

       return0;

}

tarena@tarena-virtual-machine:~/day48/day02$./a.out

456

world

456

world

2) 局部特化:针对部分类型参数所做的特化。

举例:

#include<iostream>

using namespace std;

//通用模板

template<typename A,typename B>

class X{

public:

    X(void){

       cout<<"X<A,B>"<<endl;

    }

private:

    Am_a;

    Bm_b;

};

//完全特化

template<>

class X<int,short>{

public:

   X(void){

       cout<<"X<int,short>"<<endl;

    }

private:

   int m_a;

   short m_b;

};

//局部特化

template<typename A>

class X<A,short>{

public:

   X(void){

       cout<<"X<A,short>"<<endl;   

    }

private:

    Am_a;

   short m_b;

};

//局部特化

template<typename A>

class X<A,A>{

public:

   X(void){

       cout<<"X<A,A>"<<endl;   

    }

private:

    Am_a;

    Am_b;

};

//局部特化

template<typename A>

class X<A,A*>{

public:

   X(void){

       cout<<"X<A,A*>"<<endl;   

    }

private:

    Am_a;

   A* m_b;

};

//局部特化

template<typename A,typename B>

class X<A*,B*>{//都是指针类型

public:

   X(void){

       cout<<"X<A*,B*>"<<endl;    

    }

private:

   A* m_a;

   B* m_b;

};

template<typename A>

class X<A*,A*>{//都是指针类型

public:

   X(void){

       cout<<"X<A*,A*>"<<endl;   

    }

private:

   A* m_a;

   A* m_b;

};

int main(void){

   //选择顺序:完全特化>局部特化>通用版本

   X<int,short>x1;//完全特化

   X<double,short>x2;//局部特化

   X<char,short>x3;//局部特化

   X<int,double>x4;//通用模板

   X<int,int>x5;//两个类型参数一样时

   X<double,double>x6;

   X<double,double*>x7;//前者普通类型,后者指针

   X<int*,double*>x8;//都是指针

   X<short**,char*****>x9;//都是指针

   X<int*,int*>x10;//同类型指针

}

8、智能指针

1)利用局部对象的析构函数销毁堆对象

2)通过操作符重载模拟普通指针的用法;

3)通过类型参数泛型化所维护的堆对象

4)通过转移语义避免浅拷贝和深拷贝的矛盾

5)通过局部特化区分单个对象和对象数组

Auto.cpp

#include <errno.h>

#include <cstdio>

#include <cstring>

#include <iostream>

#include <memory>

using namespace std;

class A {

public:

       A(void) {

              cout<< "A构造:"<< this << "->"

                     <<sizeof (*this) << endl;

       }

       ~A(void) {

              cout<< "A析构:"<< this << "->"

                     <<sizeof (*this) << endl;

       }

       voidprint (size_t i) const {

              cout<< m_data[i] << endl;

       }

       intm_data[1024];

};

template<typename T>

class AutoPtr {

public:

       AutoPtr(T* p = NULL) : m_p (p) {}

       AutoPtr(AutoPtr& that) :

              m_p(that.release ()) {}

       AutoPtr&operator= (AutoPtr& rhs) {

              if(&rhs != this)

                     reset(rhs.release ());

              return*this;

       }

       ~AutoPtr(void) {

              deletem_p;

       }

       T&operator* (void) const {

              return*m_p;

       }

       T*operator-> (void) const {

              return&**this;

       }

private:

       T*release (void) {

              T*p = m_p;

              m_p= NULL;

              returnp;

       }

       voidreset (T* p) {

              if(p != m_p) {

                     deletem_p;

                     m_p= p;

              }

       }

       T*m_p;

};

template<typename T>

class AutoPtr<T[]> {

public:

       AutoPtr(T* p = NULL) : m_p (p) {}

       AutoPtr(AutoPtr& that) :

              m_p(that.release ()) {}

       AutoPtr&operator= (AutoPtr& rhs) {

              if(&rhs != this)

                     reset(rhs.release ());

              return*this;

       }

       ~AutoPtr(void) {

              delete[]m_p;

       }

       T&operator* (void) const {

              return*m_p;

       }

       T*operator-> (void) const {

              return&**this;

       }

private:

       T*release (void) {

              T*p = m_p;

              m_p= NULL;

              returnp;

       }

       voidreset (T* p) {

              if(p != m_p) {

                     delete[]m_p;

                     m_p= p;

              }

       }

       T*m_p;

};

void foo (void) {

       /*

       Aa, *pa = &a;

       pa->m_data[0]= 12345;

       (*pa).print(0);

       */

//     A*pa = new A;

       AutoPtr<A>pa (new A);

       pa->m_data[0]= 12345;

//     pa.operator->()->m_data[0]= 12345;

       (*pa).print(0);

//     pa.operator*().print(0);

       AutoPtr<A>pb = pa; // 拷贝构造

       ++pb->m_data[0];

       (*pb).print(0); // 12346

       AutoPtr<A>pc (new A);

       pc->m_data[0]= 22222;

       pb= pc; // 拷贝赋值

       ++pb->m_data[0];

       (*pb).print(0); // 12346

       FILE*fp = fopen ("none", "r");

       if(! fp) {

//            deletepa;

              throwerrno;

       }

       //...

       fclose(fp);

//     deletepa;

}

int main (void) {

       /*

       try{

              foo();

       }

       catch(int ex) {

              cout<< strerror (ex) << endl;

              return-1;

       }

       cout<< "成功!"<< endl;

       */

//     AutoPtr<A[]>pa (new A[3]);

       AutoPtr<A>pa (new A);

       /*

       auto_ptr<T>

       */

       return0;

}

tarena@tarena-virtual-machine:~/day48/day02$./a.out

A构造:0x979f008->4096

A析构:0x979f008->4096

(Malloc.c   New.cpp 辅助理解)

Malloc.c

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

int main (void) {

       char*pc = (char*)malloc (1024);

       strcpy(pc, "Hello, Memory !");

       printf("%p->%s, %p\n", pc, pc, pc + 512);

       printf("第一次free...\n");

       free(pc + 512);

       printf("第二次free...\n");

       free(pc);

       return0;

}

已放弃 (核心已转储)

New.cpp

#include <cstdlib>

#include <iostream>

using namespace std;

class A {

public:

//     ~A(void) {}

       staticvoid* operator new[] (size_t size){

              void*p = malloc (size);

              cout<< p << "->" << size << "字节"

                     <<endl;

              returnp;

       }

       staticvoid operator delete[] (void* p) {

              cout<< p << endl;

              free(p);

       }

       stringm_data;

};

int main (void) {

       A*pa = new A[3];

       cout<< "pa=" << pa << endl;

       cout<< *((int*)pa-1) << endl;

       delete[]pa;

//     deletepa;

       return0;

}

tarena@tarena-virtual-machine:~/day48/day02$./a.out

0x978b008->16字节

pa=0x978b00c

3

0x978b008

C++2011中一般不推荐使用auto_ptr,而是代之以smart_ptr。

三、模板的其他特性

1、缺省参数

1)类模板的模板参数可以带有缺省值,实例化该模板时,如果提供了相应的实参则忽略缺省值,反之则以缺省作为对应形参的值。

2)如果某个模板参数带有缺省值,那么它后面的所有参数必须都带有缺省值。

3)C++98不允许为函数模板的模板参数指定缺省值,但是C++11允许。

(模板参数是在编译期间作用)

GCC<4.8 :  g++ default.cpp -std=c++0x

GCC>=4.8 :  g++ default.cpp –std=c++11

4)对函数模板,如果模板参数的缺省值与隐式推断的类型不一致,以隐式推断的类型为准,忽略其缺省值。

举例:videfault.cpp

#include <iostream>

#include <typeinfo>

using namespace std;

/*

template<typename A = int,

       typenameB = double, typename C = string>

*/

template<typename A,

       typenameB = double, typename C = string>

/*

template<typename A = int,

       typenameB, typename C = string>

template<typename A = int,

       typenameB = double, typename C>

*/

class X {

public:

       staticvoid foo (void) {

              cout<< typeid (A).name () << ' '

                     <<typeid (B).name () << ' '

                     <<typeid (C).name () << endl;

       }

};

int _x = 200;

void foo (int x, int y = /*x*/_x) {

       cout<< x << ' ' << y << endl;

}

template<typename A, typename B = A>

//template<typename A = B, typename B =int>

class Y {

public:

       staticvoid foo (void) {

              cout<< typeid (A).name () << ' '

                     <<typeid (B).name () << endl;

       }

};

template<typename A = int,

       typenameB = double, typename C = string>

void fun (void) {

       cout<< typeid (A).name () << ' '

              <<typeid (B).name () << ' '

              <<typeid (C).name () << endl;

}

template<typename T = int>

void bar (T arg) {

       cout<< typeid (arg).name () << endl;

}

int main (void) {

       X<char,short, long>::foo (); // c s l

       X<char,short>::foo (); // c s Ss

       X<char>::foo(); // c d Ss

//     X<>::foo(); // i d Ss

       foo(100);

//     PUSH... 100

//     PUSH... _x

       Y<longlong>::foo ();

//     Y<>::foo();

       fun<char,short, long> ();

       fun<char,short> ();

       fun<char>();

       fun<>();

       fun();

       bar(1.23);

       return0;

}

tarena@tarena-virtual-machine:~/day48/day02$./a.out

c s l

c s Ss

c d Ss

100 200

x x

c s l

c s Ss

c d Ss

i d Ss

i d Ss

d

2、非类型参数

1)模板除了可以接受类型参数以外,也可以接受非类型参数,即数值参数。非类的非类型参数不能用typename声明,而要注明其具体类型,而且传递给模板非类型参数的实参必须是常量、常量表达式,或者带有常属性(const)的变量,但是不能同时具有挥发性(volatile)。

原因:(模板参数是在编译期间作用)

vi array.cpp

#include <iostream>

#include <iomanip>

using namespace std;

template<typename T = int, size_t S =3>

class Array {

public:

       T&operator[] (size_t i) {

              returnm_a[i];

       }

       Tconst& operator[] (size_t i) const {

              returnconst_cast<Array&> (*this)[i];

       }

       Tm_a[S];

};

int square (int x) {

       returnx * x;

}

template<int x>

int square (void) {

       returnx * x;

}

/*

int square (void) {

       return100;

}

*/

int main (void) {

       Array<int>a;

       /*

       a.m_a[0]= 10;

       a.m_a[1]= 20;

       a.m_a[2]= 30;

       */

       a[0]= 10; // a.operator[] (0) = 10;

       a[1]= 20;

       a[2]= 30;

       Array<int>const b = a;

       /*

       cout<< b.m_a[0] << ' ' << b.m_a[1] << ' '

              <<b.m_a[2] << endl;

       */

       cout<< b[0] << ' ' << b[1] << ' '

              <<b[2] << endl;

       Array<string>c;

       c[0]= "北京";

       c[1]= "上海";

       c[2]= "广州";

       cout<< c[0] << ' ' << c[1] << ' '

              <<c[2] << endl;

       intconst /*volatile */x = 2, y = 1;

       Array<Array<int,x+y+1>, 1+1+1> d;

//     Array<Array<int,2+1+1>, 1+1+1> d;

       for(int i = 0; i < 3; ++i)

              for(int j = 0; j < 4; ++j)

                     d[i][j]= i * 4 + j + 1;

//     d.operator[](i).operator[](j)= ...;

       for(int i = 0; i < 3; ++i) {

              for(int j = 0; j < 4; ++j)

                     cout<< setw (2) << d[i][j]

                            <<' ';

              cout<< endl;

       }

       Array<Array<Array<int>> > e;

       cout<< square (10) << endl;

       cout<< square<10> () << endl;

       return0;

}

tarena@tarena-virtual-machine:~/day48/day02$./a.out

10 20 30

北京 上海广州

 1 2  3  4

 5 6  7  8

 9 1011 12

100

100

2) 向模板传递字符串形式的非类型参数:形参必须使用字符指针,实参必须使用非静态全局字符数组

3) 模板的非类型参数只能使用整数类型:[signed / unsigned]char /short / int /long

不能使用浮点数类型:float/ double

举例 vinotype.cpp

#include <iostream>

using namespace std;

template<char* str>

void foo (void) {

       cout<< str << endl;

}

template</*float*/char x>

void bar (void) {

       cout<< x << endl;

}

char g_ca[] = "abc";

int main (void) {

       foo<g_ca>();

       return0;

}

tarena@tarena-virtual-machine:~/day48/day02$./a.out

abc

3、typename

在模板被实例化之前,模板参数所表示的具体类型并不确定。编译器会把依赖于模板参数的嵌套类型(内部)类型理解为某个类的静态成员变量。当其“看到”用该变量定义其它变量的代码时,会报告错误。typename关键字可以“告诉”编译器,其后的标识符不是静态变量而是某种类型。编译器就会将有关该标识符的类型检查推迟到模板实例化的过程中,避免编译错误-----解决嵌套依赖。

class作用:1、声明类 2、声明模板的类型参数

typename作用: 1、解决嵌套依赖  2、声明模板的类型参数

举例 vi typename.cpp

#include <iostream>

using namespace std;

class A {

public:

       typedefunsigned int uint;

       classB {};

};

template<typename T>

void foo (void) {

       typenameT::uint u;

       typenameT::B b;

}

void bar (void) {}

int main (void) {

       A::uintu;

       A::Bb;

       foo<A>();

       return0;

}

4、typename关键字

1)声明函数模板和类模板

template<….>….

2)解决嵌套模板:依赖于模板参数的类型的内部模板

在模板代码中,通过依赖于模板参数的对象、引用或指针,访问其带有模板特性的成员,需要使用template关键字显示指明其后的名称是一个模板,避免编译器将模板参数表的左右尖括号理解为小于号和大于号,导致编译失败。

举例:template.cpp

#include <iostream>

#include <typeinfo>

using namespace std;

template<typename T>

class A {

public:

       //嵌套模板函数

       template<typenameU>

       voidfoo (void) const {

              Uvar;

              cout<< typeid (m_var).name () << ' '

                     <<typeid (var).name () << endl;

       }

private:

       Tm_var;

};

template<typename T>

void bar (T const& a, T const* b) {

       a.templatefoo<double> ();

       b->templatefoo<double> ();

}

int main (void) {

       A<int>a;

       a.foo<double>();

       bar(a, &a);

       return0;

}

tarena@tarena-virtual-machine:~/day48/day02$./a.out

i d

i d

i d

0 0