c++_狄泰总结05泛型编程((函数/类模板)


# 加粗样式# 泛型编程==》实际编程技术

为什么需要泛型编程?
例子:交换宏代码块
定义宏代码块
优点:代码复用,适合所有类型
缺点:编译器不知道其存在(在预处理阶段替换了),不对其进行类型检查

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

需求:1)代码复用(不同类型都能适用) 2) 进行类型检查

==》引出c++支持泛型编程(不考虑具体数据类型的编程方式)

**

1)函数模板(C++泛型编程的一种)

**
1.1)特点:
**a)可调用不同类型函数进行调用
b)类型可被参数化
c)函数模板能够根据实参对参数类型推导,也能支持显示指定参数类型
d)函数模板可以定义多个不同类型的参数
e)函数模板可以像普通函数一样被重载
f)函数模板只支持类型参数完全特化

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

1.2)语法:
a) template 关键字表示告诉编译器开始泛型编程
b) typename 关键字用于声明泛值类型 < typename T> 告诉编译器T是一个泛指类型

template < typename T>
void Swap(T& a, T& b)
{	
	T t = a;
	a = b;
	b = t;
}

1.3)函数模型的使用
a)自动类型推导: Swap(3,5)
b) 具体类型显示调用(显示指定参数类型):Swap(3,5)

int a=3;
int b=5;
Swap(3,5)		//自动类型推导
Swap<int>(3,5)		//具体类型显示调用

1.4)深入理解函数模板
泛型编程:为什么可以在不知类型的情况下,编译器从函数模板通过具体类型产生不同的函数?
=》编译器会对函数模板进行两次编译
对模板代码本身进行编译==》检查语法错误
实际调用函数模板时,将参数替换后的代码进行编译

=>注意事项:
编译器认为函数模板并不是一个函数,而是一个模子
==>函数模板本身不允许隐式类型转换
===>在调/使用函数模板时,自动类型推导必须严格匹配
===>显示类型指定时,能够进行隐式类型转换

通过函数地址证明编译器会根据不同类型进行具体调用

//定义函数类型
typedef void(FuncI)(int&, int&);
typedef void(FuncD)(double&, double&);
typedef void(FuncT)(Test&, Test&);

class Test
{
    Test(const Test&);	//面定义了拷贝构造函数,且默认等级是private,证明该对象不能拷贝构造在赋值符号左边
public:
    Test()
    {
    }
};

template < typename T >
void Swap(T& a, T& b)
{
    T c = a;
    a = b;
    b = c;
}

int main()
{
//用 swap函数对指针进行初始化 pi指向具体函数
    FuncI* pi = Swap;    // 因为FuncI为int类型 编译器自动推导 T 为 int
    FuncD* pd = Swap;    // 编译器自动推导 T 为 double
     //这里反映了会进行二次编译,由于test类里面定义了拷贝构造函数,且默认等级是private,当进行具体Swap函数具体调用时,二次编译发现class是private,不可用的
    // FuncT* pt = Swap;    // 编译器自动推导 T 为 Test	
   
    
    cout << "pi = " << reinterpret_cast<void*>(pi) << endl;		//强制类型转换,重解释pi的值,指针跟void*
    cout << "pd = " << reinterpret_cast<void*>(pd) << endl;		
    //输出结果pi 跟pd值不一样 =》有了两个swap函数
    // cout << "pt = " << reinterpret_cast<void*>(pt) << endl;
    
    return 0;
}

1.5)多参数函数模板
1.5.1特点:
a)无法推导返回值类型
b)可以从左向右部分指定类型参数
c)工程中将返回值参数作为第一个类型参数
**
语法:

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的类型,T2,T3编译器自动给推导

    // T1 = double, T2 = float, T3 = double
    double r2 = Add<double, float>(0.5, 0.8); //==》显示指定T1的类型double,T2为float,T3编译器自动给推导
}

1.6)函数模板可以像普通函数一样被重载
问:函数重载与函数模板会怎么样?
a)c++编译器优先考虑普通函数,如果参数不太匹配,才会考虑用函数模板
b) 通过空模板实参列表,可以强制限定编译器只采用函数模板,而不采用函数重载的普通函数

 double r2 = Max<>(0.5,0.8)

实际例子

using namespace std;
//这3个Max 发生函数重载
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;
}

1.7)函数模板的完全特化实例

template
< typename T >
bool Equal(T a, T b)	//特殊情况,浮点数在内存里是不精确的,所以将这种double的情况特殊化
{
    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;
    cout << Equal( 0.001, 0.001 ) << endl;	//当有全家重载函数,编译器优先使用全局函数 cout << "bool Equal(double a, double b)" << endl;
     cout << Equal<>( 0.001, 0.001 ) << endl; //增加<> ,强制编译器采用模板,编译器采用这个cout << "bool Equal<double>(double a, double b)" << endl;
    return 0;
}

2)类模板

泛型编程的思想也可以作用于类
2.1)类模板的特性
**a)以相同的方式处理不同类型的数据,非常适合编写数据结构相关的代码
b)类模板在使用时必须显示指定具体类型,无法自动推导,使用具体类型 定义对象
c) 声明的泛指类型T可以出现在内类模板的任意地方
d)编译器对待类模板的角度,跟函数模式一样
e)类模板可以定义多个不同类型的参数
f)类模板支持部分特化和完全特化(部分特化与完全特化的区别 部分特化)
=》特化的本质是模板的分开实现
=》函数名模板支持完全特化

=》工程中用函数特化替代函数重载**

问1:类模板的特(殊)化是什么?
特化只是模板根据实际需要分开实现,本质上是同一个类模板
特化类模板的使用方式是统一的(依旧需要显式调用)
必须显示指定么一个类型参数

**问2:类模板的特化不会看作函数重载吗?
答:编译器会认为这两个都是同一个模板,只是看要选择哪一个实现

问3:类模板特化与重定义有区别吗**
重定义:
一个类模板+一个新类(或两个类模板)
使用的适合需要手动考虑如何选择
特化:(考虑特殊的情况,模板应该怎么实现功能)
以统一的方式使用类模板和特化类
编译器自动优先选择特化类

问4:怎么区分部分特化与完全特化?
部分特化 :

template	//类模板特(殊)化为一个参数=> 如果俩个参数一样,编译器优先采用这个模板特化
< typename T >
class Test < T, T >    // 当 Test 类模板的两个类型参数完全相同时,使用这个实现

完全特化:

template					//类模板完全特化
<  >
class Test < void*, void* >    // 当 T1 == void* 并且 T2 == void* 时

2.2)类模板的工程应用
a)类模板必须在头文件中定义
b)类模板不能分开实现在不同文件中
c)类模板外部定义的成员函数需加上模板<>声明
对模板代码本身进行编译==》检查语法错误
实际调用函数模板时,将参数替换后的代码进行编译

::operator.h
ifndef _OPERATOR_H_
#define _OPERATOR_H_

template < typename T >		//a)类模板必须在头文件中定义
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 >			//c)类模板外部定义的成员函数需加上模板<>声明
T Operator<T>::add(T a, T b)
{
    return a + b;
}

::58.2.cpp
using namespace std;
int main()
{
//,使用具体类型 <Type>定义对象
   Operator<int> op1;		//b)类模板在使用时必须显示指定具体类型,无法自动推导,
    cout << op1.add(1, 2) << endl;
  }

**

类模板特化实例


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

//f)类模板支持部分特化和完全特化(部分特化与完全特化的区别 部分特化)
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;
    }
};

template	//类模板特(殊)化为一个参数=> 如果俩个参数一样,编译器优先采用这个模板特化
< typename T >
class Test < T, T >    // 当 Test 类模板的两个类型参数完全相同时,使用这个实现
{
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 < void*, void* >    // 当 T1 == void* 并且 T2 == 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;	
	/*template
	< typename T1, typename T2 >
	class Test < T1*, T2* > */
	
    Test<long, long> t2;
	/*template
	< typename T >
	class Test < T, T >    // 当 Test 类模板的两个类型参数完全相同时,使用这个实现
	*/
    
	Test<void*, void*> t3;
    /*template
	<  >
	class Test < void*, void* >    // 当 T1 == void* 并且 T2 == void* 时 */
   
    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;
}

3)数组类模板

预备知识:
模板参数可以是数值型参数(非类型参数)

template 
< typename T ,int N>
void func()
{
	T a[N]; 	//使用模板参数定义局部数组
} 
func<double, 10>();

模板参数本质:
模板参数是在编译阶段被处理的单元,因此,在编译阶段必须准确无误的唯一确定。
数值型模板参数的限制
变量不能作为模板参数
浮点数不能作为模板参数 //在内存中的表示是不精确的
类对象不能作为模板参数 //类对象不能唯一确定

案例1:用最高效的方式来求 1+2+3+…+N

  1. for循环

2)等差数列
3)模板技术
依赖技术: 类模板技术 ,模板完全特化技术 数值型参数模板

#include <iostream>
#include <string>

using namespace std;

template
< typename T, int 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;
    }
}

template
< int N >
class Sum	//递归定义
{
public:
	//static const int VALUE =0 (const int VALUE =0)定义一个常量,用static来限定
	/*因此要么存储在全局数据区,要么放在符合表 => value值会直接进入符合表,
	由于static 修饰,value会存放在全局数据区  => 证明解决
    static const int VALUE = Sum<N-1>::VALUE + N;  //递归定义 Sum<N-1>::VALUE + N;编译器会先去求Sum<N-1>::VALUE 的值*/
};

template
< >
class Sum < 1 >		//sum 模板类的特化实现(完全特化)
{
public:
    static const int VALUE = 1;
};


int main()
{
/*这样最高效,只用了一条语句,计算过程在编译阶段,编译完成后,结果就确定了, Sum<10>::VALUE既没有加减乘除操作,也没有函数调用过程
VALUE是一个常量,在编译阶段已经确认了
	当遇到Sum<10>::VALUE时,编译器会去template < int N >class Sum 里去求值,后面发现是递归求值,不断往下走,直到遇到完全特化 template< > class Sum < 1 > ,最终求粗这个值,并把这个值放入符合表中

    cout << "1 + 2 + 3 + ... + 10 = " << Sum<10>::VALUE << endl;	//这里只做了访问常量
    cout << "1 + 2 + 3 + ... + 100 = " << Sum<100>::VALUE << endl;
    
    return 0;
}

案例2: 数值型参数类模板

:array.h
#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

案例3:堆数组类模版构建(简易的线性表数据结构)

堆数组类模版:在栈上面分配空间
优势:仅开发一次,后面就可以无限次使用 ==》c++代码复用

::heaparray.h
#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();	//由于构造函数是private,不想给继承,所以可以不写virtual
};

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()
{//需要用哪个类型,修改 char 就行
 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;
}

4)智能指针类模板

=>通过类模板,可以创建各种类型的智能指针
智能指针的意义:
智能指针是C++中自动内存管理的主要手段
智能指针能尽量避开内存相关的问题 内存泄漏不容易出现() /多次指针释放,立刻死或迟一些死(无法帝国为问题)
智能指针在各个平台上都有不同的表现形式(QT有一些内存管理的思想是STL没有的)
STL和QT都提供了对智能指针的支持

案例:编写智能指针类模板 ==》参照STL auto_ptr的设计

#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

::test.cpp
#include <iostream>
#include <string>
#include "SmartPointer.h"

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()
{
    SmartPointer<Test> pt(new Test("D.T.Software"));
    
    cout << "pt = " << pt.get() << endl;
    
    pt->print();
    
    cout << endl;
    
    SmartPointer<Test> pt1(pt);
    
    cout << "pt = " << pt.get() << endl;
    cout << "pt1 = " << pt1.get() << endl;
    
    pt1->print();
    
    return 0;
}
输出:
Hello, D.T.Software.
pt = 0x1ab5c20
I'm D.T.Software.

pt = 0
pt1 = 0x1ab5c20
I'm D.T.Software.
Goodbye, D.T.Software.

STL中的智能指针(本质是对象)

解决内存泄漏
缺点:不能指向堆对象/变量 不算缺点:指向堆对象可以用其它,例如案例3的heapArray
在这里插入图片描述

案例1:STL auto_ptr 使用案例



#include <iostream>
#include <string>
#include <memory>	//auto_ptr 在这个里面
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()
{
    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;
    cout << "pt1 = " << pt1.get() << endl;
    
    pt1->print();
    
    return 0;	//生命周期结束,调用析构函数,销毁指向的内存空间,智能指针本质是对象
}

输出:
Hello, D.T.Software.
pt = 0x806c20
I'm D.T.Software.

pt = 0
pt1 = 0x806c20
I'm D.T.Software.
Goodbye, D.T.Software.

QT中的智能指针

1)最大特点:对象被销毁时,被自动置为空指针 =》可以使用多个Qpoint指向同一个对象,当这个对象被消毁,其它指针都置空 =》避免多次释放和野指针问题
2)析构不会自动删除堆空间里的对象,要自己手动删除
在这里插入图片描述

案例2 QPoint跟QSharePoint的实验

#include <QPointer>
#include <QSharedPointer>
#include <QDebug>

class Test : public QObject	//qt中定义的类都必须继承qt的顶层父类 QObject
{
    QString m_name;
public:
    Test(const char* name)
    {
        qDebug() << "Hello, " << name << ".";	//qt里的调试输出 ,c++里是cout

        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);        //QPoint可以多个指针指向同一个对象

    pt->print();
    pt1->print();
    pt2->print();

    delete pt;					//需要手动释放

    qDebug() << "pt = " << pt;	//输出为都null,释放后指向同一个对象的指针都置空
    qDebug() << "pt1 = " << pt1;
    qDebug() << "pt2 = " << pt2;

    qDebug() << endl;
//QSharedPointer 的设计非常奇妙
    QSharedPointer<Test> spt(new Test("Delphi Tang"));  Qsharepoint当指向一个对象时,引用计数+1
    QSharedPointer<Test> spt1(spt);
    QSharedPointer<Test> spt2(spt);

    spt->print();		//当用完一个后,指针减1
    spt1->print();		//这里3个指针值同一个对象
    spt2->print();	
    //当这个引用为0,只调用一次析构函数(因为引用技术)

    return 0;
}

单例类模板(设计模式:这个类最多只有一个对象)

(开发中最常用的设计模式之一)

搭建思路:要控制对象的数目,需对外隐藏构造函数,不能给外部直接构造
1)将构造相关的函数(构造函数 拷贝构造函数 操作符重载函数)的访问属性设为private
2)定义一个类指针,且访问属性为private,不可被外部访问,只能是类的成员函数进行访问,并判断是否为空,如果为空,才进行创建对象,用该指针指向新创建的对象,否则返回当前指向类对象的指针

需要使用单例模式时:
必须定义一个静态成员变量
定义一个静态成员函数

案例1 C++ 单例类构建

#include <iostream>
#include <string>

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

案例2 单例类改造单例类模板

在某个要使用单例模式 的类里加这个:
class SObject
{
friend class Singleton<SObject>;    // 当前类需要使用单例模式
 SObject(const SObject&);		`在这里插入代码片`//与构造(构造函数、拷贝构造函数、操作符重载函数)相关函数设置为private
 SObject& operator= (const SObject&);
    
    SObject()
    {
    }
 }
使用:
SObject* s = Singleton<SObject>::GetInstance();

需要使用单例模式时:
必须定义一个静态成员变量
定义一个静态成员函数
但是单例类里的静态成员变量跟静态成员函数跟我们实际要用到的功能没有关系
=>改进:将单例模式相关的 代码取出来,开发单例类,当要用到单例类时,直接使用单例类模板

::Singleton.h
#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

::main.cpp
#include <iostream>
#include <string>
#include "Singleton.h"

using namespace std;

class SObject
{
//如果一个类想要使用单例模式,按这个来使用就可以了
 	//声明class Singleton<SObject>(这个类模板) 是 class SObject 的友元,那么Singleton<SObject>里的所有成员可以打破访问属性限制访问class SObject里的所有成员 =》可以访问这个构造函数SObject()
    friend class Singleton<SObject>;    // 当前类需要使用单例模式
    
    SObject(const SObject&);
    SObject& operator= (const SObject&);
    
    SObject()
    {
    }
public:
    
    void print()
    {
        cout << "this = " << this << endl;
    }
};

int main()
{
//Singleton<SObject>::GetInstance(); 通过Singleton<>类模板,得到SObject类的GetInstance()的对象地址
    SObject* s = Singleton<SObject>::GetInstance();
    SObject* s1 = Singleton<SObject>::GetInstance();
    SObject* s2 = Singleton<SObject>::GetInstance();
    
    s->print();
    s1->print();
    s2->print();
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值