深入理解函数模板和类模板

目录

1、函数与宏

2、函数模板

3、模板的两次编译

4、多参数函数模板

5、重载函数模板

6、C++中的类模板 

7、模板的特化

8、继承模板类访问基类成员

9、typename与class

10、数组类模板

11、单例类模板

12、判断变量是不是指针


1、函数与宏

宏代码块

优点:代码复用,适合所有的类型 。缺点:编译器不知道宏的存在,缺少类型检查 

函数 

优点:真正的函数调用,编译器对类型进行检查 

缺点:根据类型重复定义函数,无法代码复用 

#include <iostream>  

using namespace std;  
  
#define SWAP(t, a, b)    \  
do                       \  
{                        \  
    t c = a;             \  
    a = b;               \  
    b = c;               \  
}while(0)  
  
  
void Swap(int& a, int& b)  
{  
    int c = a;  
    a = b;  
    b = c;  
}  
  
void Swap(double& a, double& b)  
{  
    double c = a;  
    a = b;  
    b = c;  
}  
  
void Swap(string& a, string& b)  
{  
    string c = a;  
    a = b;  
    b = c;  
}  
  
int main()  
{  
    int a = 0;  
    int b = 1;  
      
    Swap(a, b);  
      
    cout << "a = " << a << endl;  
    cout << "b = " << b << endl;  
      
    double m = 2;  
    double n = 3;  
      
    Swap(m, n);  
      
    cout << "m = " << m << endl;  
    cout << "n = " << n << endl;  
      
    string d = "Nyist";  
    string t = "Wss";  
      
    Swap(d, t);  
      
    cout << "d = " << d << endl;  
    cout << "t = " << t << endl;  
      
    return 0;  
} 

泛型编程的概念 :不考虑具体数据类型的编程方式 

2、函数模板

C++中的函数模板 :—种特殊的函数可用不同类型进行调用 ,看起来和普通函数很相似,区别是类型可被参数化 

使用函数模板时:可以自动类型推导调用 ,也可以具体类型显示调用

本质:模板参数是在编译阶段被处理的单元,因此,在编译阶段必须准确无误的唯—确

#include <iostream>
using namespace std;

// template关键字用于声明开始进行泛型编程
// typename关键字用于声明泛指类型
template <typename T>
void Swap(T& a, T& b) // Swap泛型写法中的T不是—个具体的数据类型,而是泛指任意的数据类型。
{
    T t = a;
    a = b;
    b = t;
}

template < typename T >
void Sort(T a[], int len)
{
    for(int i=0; i<len; i++)
    {
        for(int j=i; j<len; j++)
        {
            if( a[i] > a[j] )  // comparable
            {
                Swap(a[i], a[j]);
            }
        }
    }
}
template < typename T >
void Println(T a[], int len)
{
    for(int i=0; i<len; i++)
    {
        cout << a[i] << ", ";
    }

    cout << endl;
}

template < typename T, int N >  // N不能接收变量
void func()
{
    T a[N] = {0};// 使用模板参数定义局部数组

    for(int i=0; i<N; i++)
    {
        a[i] = i;
    }

    for(int i=0; i<N; i++)
    {
        cout << a[i] << endl;
    }
}

int main()
{
    int a = 0;
    int b = 1;

    Swap(a, b); // 自动推导

    float c = 2;
    float d = 3;

    Swap<float>(c, d); // 显式调用

    func<double, 10>();


    int arr[5] = {5, 3, 2, 4, 1};
    Println(arr, 5);
    Sort(arr, 5);
    Println(arr, 5);

    string s[5] = {"Java", "C++", "Pascal", "Ruby", "Basic"};

    Println(s, 5);
    Sort(s, 5);
    Println(s, 5);

    return 0;
}

 

 

3、模板的两次编译

编译器从函数模板通过具体类型产生不同的函数 ,编译器会对函数模板进行两次编译:对模板代码本身进行编译 ,对参数替换后的代码进行编译

函数模板本身不允许隐式类型转换 ,自动推导类型时,必须严格匹配 ;显式类型指定时,能够进行隐式类型转换 

#include <iostream>  
  
using namespace std;  
  
class Test  
{  
    Test(const Test&);  // private,不能拷贝构造
public:  
    Test(){}  
};  
  
template < typename T >  
void Swap(T& a, T& b)  // 先会对模板代码本身进行编译,确保无语法错误,替换参数类型后再次编译,确保再次无语法错误
{  
    T c = a;  // T如果是Test就会编译报错,因为Test拷贝构造函数访问权限为private
    a = b;  
    b = c;  
}  
  
typedef void(FuncI)(int&, int&);  
typedef void(FuncD)(double&, double&);  
typedef void(FuncT)(Test&, Test&);  
  
int main()  
{  
    FuncI* pi = Swap;    // 编译器自动推导 T 为 int  
    FuncD* pd = Swap;    // 编译器自动推导 T 为 double  
    //FuncT* pt = Swap;  // 编译器自动推导 T 为 Test  ,编译出错,说明二次编译
      
    cout << "pi = " << reinterpret_cast<void*>(pi) << endl;  
    cout << "pd = " << reinterpret_cast<void*>(pd) << endl;  
    //cout << "pt = " << reinterpret_cast<void*>(pt) << endl;  
      
    return 0;  
}  

 

4、多参数函数模板

函数模板可以定义任意多个不同的类型参数,对于多参数函数模板 无法自动推导返回值类型 ,

可以从左向右部分指定类型参数 ,工程中将返回值参数作为第一个类型的参数

#include <iostream>
using namespace std;

template < typename T1, typename T2>
void Print(T1 a, T2 b)
{
    cout << a << "\t" << b << endl;
}

template < typename T1, typename T2, typename T3 >
T1 Add(T2 a, T3 b)
{
    return static_cast<T1>(a + b);
}

int main()
{
    Print(1, "wss");

    // 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;
}

5、重载函数模板

函数模板可以像普通函数—样被重载

C++编译器优先考虑普通函数 ,如果函数模板可以产生—个更好的匹配,那么选择模板 

可以通过空模板实参列表限定编译器只匹配模板 

#include <iostream>    
  
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;  
}  

函数模板能够根据实参对参数类型进行推导 ,函数模板支持显示的指定参数类型 

函数模板通过具体类型产生不同的函数 ,函数模板可以定义任意多个不同的类型参数 

函数模板可以像普通函数—样被重载

6、C++中的类模板 

类模板 

一些类主要用于存储和组织数据元素 ,类中数据组织的方式和数据元素的具体类型无关 。如:数组类,链表类,Stack类,Queue类,等 

C++中将模板的思想应用于类,使得类的实现不关注数据元素的具体类型,而只关注类所需要实现的功能。

C++中的类模板

以相同的方式处理不同的类型 ,在类声明前使用template进行标识 ,< typename T >用于说明类中使用的泛指类型T

声明的泛指类型T可以出现在类模板的任意地方 ,编译器对类模板的处理方式和函数模板相同 

☛ 从类模板通过具体类型产生不同的类 

在声明的地方对类模板代码本身进行编译 

在使用的地方对参数替换后的代码进行编译

类模板的使用

只能显式指定具体类型,无法自动推导 ,使用具体类型 <Type> 定义对象 → Operator<int> op1;

#include <iostream>
#include <string>

using namespace std;

string operator - (string& l, string& r)
{
    return "Minus";
}

template < typename T >
class Operator
{
public:
    T add(T a, T b)
    {
        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()
{
    Operator<int> op1; 

    cout << op1.add(1, 2) << endl;

    Operator<string> op2; // 必须重载-操作符 ,否则第二次编译错误

    cout << op2.add("a", "b") << endl;
    cout << op2.minus("a", "b") << endl;

    return 0;
}

 类模板的工程应用:类模板必须在头文件中定义 ,类模板不能分开实现在不同的文件中 ,类模板外部定义的成员函数需要加上模板<>声明 

#ifndef _OPERATOR_H_  
#define _OPERATOR_H_  
  
template < typename T >  
class Operator  
{  
public:  
    T add(T a, T b);  
    T minus(T a, T b);  
    T multiply(T a, T b);  
    T divide(T a, T b);  
};  
  
template < typename T >  
T Operator<T>::add(T a, T b)  
{  
    return a + b;  
}  

template < typename T >  
T Operator<T>::minus(T a, T b)  
{  
    return a - b;  
}  
 
template < typename T >  
T Operator<T>::multiply(T a, T b)  
{  
    return a * b;  
}  

template < typename T >  
T Operator<T>::divide(T a, T b)  
{  
    return a / b;  
}  
  
#endif  
#include <iostream>  
#include "Operator.h"  
  
using namespace std;  

int main()  
{  
    Operator<int> op1;  
      
    cout << op1.add(1, 2) << endl;  
    cout << op1.multiply(4, 5) << endl;  
    cout << op1.minus(5, 6) << endl;  
    cout << op1.divide(10, 5) << endl;  
      
    return 0;  
}  

 

7、模板的特化

类模板可以定义任意多个不同的类型参数 

类模板可以被特化 

    -指定类模板的特定实现 ,部分类型参数必须显式指定 ,根据类型参数分开实现类模板

    -特化只是模板的分开实现 ,本质上是同—个类模板 ;特化类模板的使用方式是统—的,必须显式指定每—个类型参数 

类模板的特化类型 

    -部分特化-用特定规则约束类型参数 

    -完全特化-完全显式指定类型参数  

#include <iostream>
using namespace std;

template <typename T1, typename T2>
class Test{
public:
    Test(){cout << "template <typename T1, typename T2>" << endl;}
};

// 部分特化
template <typename T>
class Test<T, T>{
public:
    Test(){cout << "Test<T, T>" << endl;}
};
// 部分特化
template <typename T>
class Test<T, int>{
public:
    Test(){cout << "Test<T, int>" << endl;}
};

// 完全特化
template <>
class Test<int, int>{
public:
    Test(){cout << "Test<int, int>" << endl;}
};


int main()
{
    Test<double, short> t0;
    Test<double, double> t1;
    Test<double, int> t2;
    Test<int, int> t3;

    return 0;
}

 

#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 T1, typename T2>
class Test <T1*, T2*>
{
public:
    void add(T1* a, T2* b)
    {
        cout << "void add(T1* a, T2* b)" << endl;
        cout << *a + *b << endl;
    }
};
// 当 Test 类模板的两个类型参数完全相同时,使用这个实现
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;
    }
};
 // 当 T1 == void* 并且 T2 == void* 时 ,完全特化
template <>
class Test < void*, void* >
{
public:
    void add(void* a, void* b)
    {
        cout << "void add(void* a, void* b)" << endl;
        cout << "Error to add void* param..." << endl;
    }
};

int main()
{
    Test<int, float> t1;
    Test<long, long> t2;
    Test<void*, void*> t3;

    t1.add(1, 2.5);

    t2.add(5, 5);
    t2.print();

    t3.add(NULL, NULL);

    Test<int*, double*> t4;
    int a = 1;
    double b = 0.1;

    t4.add(&a, &b);

    return 0;
}

 

重定义和特化的不同 

重定义 : —个类模板和—个新类(或者两个类模板) ,使用的时候需要考虑如何选择的问题 

特化 :以统—的方式使用类模板和特化类 ,编译器自动优先选择特化类

函数模板的特化: 函数模板只支待类型参数完全特化 

工程中的建议:当需要重载函数模板时,优先考虑使用模板特化 ,当模板特化无法满足需求,再使用函数重载!

template < typename T >  // 函数模板定义
bool Equal(T a, T b)
{
	return a == b;
}
template < >             // 函数模板完全特化
bool Equal<void*>(void* a, void* b)
{
	return a == b;
}
#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;
    }
};
// 重定义
class Test_Void
{
public:
    void add(void* a, void* b)
    {
        cout << "void add(void* a, void* b)" << endl;
        cout << "Error to add void* param..." << endl;
    }
};

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()
{
    cout << Equal( 1, 1 ) << endl;            // bool Equal(T a, T b)
    cout << Equal<>( 0.001, 0.001 ) << endl;  // <>使用模板,否则会优先选择重载函数

    return 0;
}

有趣的面试题

最高效的方法求 1 + 2 + 3 + … + N 的值!

#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;  // VALUE进入符号表,且会分配空间在全局数据区
};  
// 先编译发现语法无误,带入参数10后再次编译,即编译期就可以确定VALUE的值  
  
int main()  
{   
    cout << "1 + 2 + 3 + ... + 10 = " << Sum<10>::VALUE << endl;  // 仅仅访问常量值,编译阶段已经得到结果
    cout << "1 + 2 + 3 + ... + 100 = " << Sum<100>::VALUE << endl;  

    //cout << sizeof(Sum<10>) << endl; // 1 ,可以说Sum是空类
      
    return 0;  
}  

 

8、继承模板类访问基类成员

继承模板类要使用this指针或Base::访问基类成员

本质就是二次编译

第一阶段编译时对模板代码本身编译,确定语法等无误。此时子类继承父类,父类也是第一阶编译还未成型被忽略

此时子类使用父类成员时必须显式指明。如:使用this表明变量就是本对象里有的 或 加上作用域指明父类有这个变量

第二阶段编译类型参数被替换,父子关系模型确认

#include <iostream>

using namespace std;

template <typename T>
class Base
{
protected:
    T* m_array;
    int length;

public:
    void print()
    {
        cout << "OK" << endl;
    }
};

template <typename T, int N>
class Derived : public Base<T>
{
    T m_space[N];
public:
    Derived()
    {
        this->Base<T>::m_array = m_space; // this->m_array = m_space;
        this->length = 0; // Base<T>::m_length = 0;
    }
};

int main()
{
    Derived<int, 5> d;

    d.print(); // OK

    return 0;
}

令人郁闷的是在VS2015中测试,不加this也可以正常使用。。。但绝不编写依赖编译器的代码。。。

 

9、typename与class

为什么模板中声明泛指类型有的使用typename,有的使用class?

如下面代码使用typename与class没有区别

#include <iostream>  
  
using namespace std;  
  
template < class T >  // template < typename T >  
class Test  
{   
public:  
    Test(T t)   
    {   
        cout << "t = " << t << endl;  
    }  
};  
  
template < class T >  
void func(T a[], int len)  
{  
    for(int i=0; i<len; i++)  
    {  
        cout << a[i] << endl;  
    }  
}  
  
int main()  
{  
    Test<string> ts("aaa");   
    string ta[]={"W","W","W","W"};  
      
    func(ta, 4);  
      
      
    Test<int> ti(100);      
    int ai[]={1, 2, 3, 4};  
      
    func(ai, 4);  
      
    return 0;  
 }  

历史上的原因

    -早期的C++直接复用class关键字来定义模板 ,但是泛型编程针对的不只是类类型 ,class关键字的复用使得代码出现二义性 

typename诞生的直接诱因

    -自定义类类型内部的嵌套类型 不同类中的同一个标识符可能导致二义性编译器无法辨识标识符究竟是什么

    -typename可以消除模板中的二义性 

#include <iostream>

using namespace std;

int a = 0;

class Test_1
{
public:
    static const int TS = 1;
};

class Test_2
{
public:
    struct TS
    {
        int value;
    };
};

template
< class T >
void test_class()
{                     // 两种理解方式 - 二义性
    T::TS * a;        // 1. 通过泛指类型 T 内部的数据类型 TS 定义指针变量 a (推荐的解读方式)
                      // 2. 使用泛指类型 T 内部的静态成员变量 TS 与全局变量 a 进行乘法操作
}


int main(int argc, char *argv[])
{
    test_class<Test_1>();
    test_class<Test_2>(); // error: missing 'typename' prior to dependent type name 'Test_2::TS'
    // 编译器默认认为Test_2::TS是一个静态成员变量而不是数据类型
    // 解决方案: typename T::TS * a;
    // typename的两个作用:1. 在模板定义中声明泛指类型 2. 明确告诉编译器其后的标识符为类型 

    // 所以此时test_class<Test_1>();会编译出错

    return 0;
}

10、数组类模板

静态数组类模板

#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  

堆数组模板类

#ifndef _HEAPARRAY_H_  
#define _HEAPARRAY_H_  
  
template  
< typename T >  
class HeapArray  
{  
private:  
    int m_length;  
    T* m_pointer;  
      
    HeapArray(int len);  
    HeapArray(const HeapArray<T>& obj);  
    bool construct();  
public:  
    static HeapArray<T>* NewInstance(int length);   
    int length();  
    bool get(int index, T& value);  
    bool set(int index ,T value);  
    T& operator [] (int index);  
    T operator [] (int index) const;  
    HeapArray<T>& self();  
    ~HeapArray();  
};  
  
template  
< typename T >  
HeapArray<T>::HeapArray(int len)  
{  
    m_length = len;  
}  
  
template  
< typename T >  
bool HeapArray<T>::construct()  
{     
    m_pointer = new T[m_length];  
      
    return m_pointer != NULL;  
}  
  
template  
< typename T >  
HeapArray<T>* HeapArray<T>::NewInstance(int length)   
{  
    HeapArray<T>* ret = new HeapArray<T>(length);  
      
    if( !(ret && ret->construct()) )   
    {  
        delete ret;  
        ret = 0;  
    }  
          
    return ret;  
}  
  
template  
< typename T >  
int HeapArray<T>::length()  
{  
    return m_length;  
}  
  
template  
< typename T >  
bool HeapArray<T>::get(int index, T& value)  
{  
    bool ret = (0 <= index) && (index < length());  
      
    if( ret )  
    {  
        value = m_pointer[index];  
    }  
      
    return ret;  
}  
  
template  
< typename T >  
bool HeapArray<T>::set(int index, T value)  
{  
    bool ret = (0 <= index) && (index < length());  
      
    if( ret )  
    {  
        m_pointer[index] = value;  
    }  
      
    return ret;  
}  
  
template  
< typename T >  
T& HeapArray<T>::operator [] (int index)  
{  
    return m_pointer[index];  
}  
  
template  
< typename T >  
T HeapArray<T>::operator [] (int index) const  
{  
    return m_pointer[index];  
}  
  
template  
< typename T >  
HeapArray<T>& HeapArray<T>::self()  
{  
    return *this;  
}  
  
template  
< typename T >  
HeapArray<T>::~HeapArray()  
{  
    delete[]m_pointer;  
}  
  
  
#endif  

main.cpp

#include <iostream>  
#include <string>  
#include "Array.h"  
#include "HeapArray.h"  
  
using namespace std;  
  
int main()  
{  
    Array<double, 5> ad;  
      
    for(int i=0; i<ad.length(); i++)  
    {  
        ad[i] = i * i;  
    }  
      
    for(int i=0; i<ad.length(); i++)  
    {  
        cout << ad[i] << endl;  
    }  
      
    cout << endl;  
      
    HeapArray<char>* pai = HeapArray<char>::NewInstance(10);  
      
    if( pai != NULL )  
    {  
        HeapArray<char>& ai = pai->self();  
          
        for(int i=0; i<ai.length(); i++)  
        {  
            ai[i] = i + 'a';  
        }  
          
        for(int i=0; i<ai.length(); i++)  
        {  
            cout << ai[i] << endl;  
        }  
    }  
      
    delete pai;  
      
    return 0;  
}  

 

 

11、单例类模板

定义—个类,使得这个类最多只能创建—个对象

要控制类的对象数目,必须对外隐藏构造函数,将构造函数的访问属性设置为private 

定义instance并初始化为NULL ,当需要使用对象时,访问instance的值 

- 如果是空值:创建对象,并用instance标记 

- 如果是非空值:返回instance标记的对象 

#include <iostream>  
  
using namespace std;  
  
class SObject  
{  
    static SObject* c_instance;  
      
    SObject(const SObject&);  
    SObject& operator= (const SObject&);  
      
    SObject()  
    {  
        //隐藏构造函数,达到无法在外部创建对象的目的
    }  
public:  
    static SObject* GetInstance();  //提供一个接口创建和访问唯一的对象
      
    void print()  
    {  
        cout << "this = " << this << endl;  
    }  
};  
  
SObject* SObject::c_instance = NULL;  
  
SObject* SObject::GetInstance()  
{  
    if( c_instance == NULL )  
    {  
        c_instance = new SObject();  
    }  
      
    return c_instance;  
}  
  
int main()  
{  
    SObject* s = SObject::GetInstance();  
    SObject* s1 = SObject::GetInstance();  
    SObject* s2 = SObject::GetInstance();  
      
    s->print();  
    s1->print();  
    s2->print();  
      
    return 0;  
}  

  

存在的问题 :需要使用单例模式时必须定义静态成员变量c_instance ,必须定义静态成员函数Getlnstance() 

解决方案 :将单例模式相关的代码抽取出来,开发单例类 模板。当需要单例类时,直接使用单例类模板

#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 "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;  
}  

12、判断变量是不是指针

C++中仍然支持C语言中的可变参数函数 ,C++编译器的匹配调用优先级: 1. 重载函数  2. 函数模板  3. 变参函数 

指针的判别

将变量分为两类:指针 vs 非指针 

利用函数模板和变参函数能够判断指针变量 

编写函数:指针变量调用时返回true ,非指针变量调用时返回false

// 函数模板与变参函数的化学反应
template  
<typename T>  
bool IsPtr(T* v) // match pointer  
{  
    return true;  
}  
  
bool IsPtr(...)  // match non-pointer  
{  
    return false;  
} 
#include <iostream>

using namespace std;

class Test
{
public:
    Test()
    {
    }
    virtual ~Test()
    {
    }
};

template
<typename T>
bool IsPtr(T* v) // match pointer
{
    return true;
}

bool IsPtr(...)  // match non-pointer 
{
    return false;
}


int main(int argc, char *argv[])
{
    int i = 0;
    int* p = &i;

    cout << "p is a pointer: " << IsPtr(p) << endl;    // 1
    cout << "i is a pointer: " << IsPtr(i) << endl;    // 0

    Test t;
    Test* pt = &t;

    cout << "pt is a pointer: " << IsPtr(pt) << endl;    // 1
        
    /*
     * error: cannot pass object of non-trivial type 'Test' through variadic function; call will abort at runtime
     */
    cout << "t is a pointer: " << IsPtr(t) << endl;    // 0 或 程序崩溃

    return 0;
}

存在的缺陷: 变参函数无法解析对象参数,可能造成程序崩溃! 

进—步的挑战: 如何让编译器精确匹配函数,但不进行实际的调用?

完美解决方案:

#include <iostream>  
  
using namespace std;  
  
class Test  
{  
public:  
    Test()  
    {  
    }  
    virtual ~Test()  
    {  
    }  
};  
  
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)) // 通过使用sizeof在编译期间就能确定ISPTR(p)的值
  
int main(int argc, char *argv[])  
{  
    int i = 0;  
    int* p = &i;  
      
    cout << "p is a pointer: " << ISPTR(p) << endl;    // true  
    cout << "i is a pointer: " << ISPTR(i) << endl;    // false  
      
    Test t;  
    Test* pt = &t;  
      
    cout << "pt is a pointer: " << ISPTR(pt) << endl;    // true  
    cout << "t is a pointer: " << ISPTR(t) << endl;    // false  
      
    return 0;  
}  

 

            

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值