设计模式 学习笔记(1) 单例模式之饿汉式

单例模式

单例模式,就是指一个类有且仅有一个对象实例。

例如做一个学生成绩管理系统,我定义了一个“系统类”,用于存储数据,类似于“数据库”的功能。这个系统类当然只能有一个对象实例了;如果有多个实例,就会导致数据不同步。

1. 饿汉式

饿汉式,简单来讲,就是预先建立一个对象,设为 null ( Java中不可以这么写, C++中可以 ),不管什么时候需要用这个类,就返回这个唯一的对象引用或者指针。

(1)保证唯一:static

这个类的对象共享这一个 static 对象,

(2)保证只用这个 static 对象

把构造函数设为 private,需要用到这个类时,用一个函数 Get_instance () 得到 static 对象的引用或指针
#include <bits/stdc++.h>

class Gragh {
private:
	int data ;
	// 饿汉式, 线程安全
	static Gragh null ;
public:
	void display () const {
		std::cout << "data = " << data << std::endl ;
	}
	// error: cannot call member function without  object // 要加 static ;非静态成员函数必须要有对象才能调用
	static Gragh &Get_instance () {
		return null ;
	}
private: 
	// 构造函数声明成私有的
	explicit Gragh () : data ( 100 ) {}
} ;
// undefined reference to null   // 不在类外声明, 会报错
Gragh Gragh::null ;     

int main () {
	Gragh &One = Gragh::Get_instance () ;  // 只能用 classname:: 因为现在还没有对象
	One.display () ;
	std::cout << "One 的地址是  :  " << &One << std::endl ;

	Gragh &Two = Gragh::Get_instance () ;
	Two.display () ;
	std::cout << "Two 的地址是  :  " << &Two << std::endl ;
	return 0 ;
}
可以发现,得到的两个引用的地址是一样的,再声明多少个引用都是一样的地址,这就保证了唯一;而且,如果直接声明构造函数 Gragh One ; 或者 Gragh *One = new Gragh () ,会编译错误,因为构造函数是私有的 private。

但是还是存在几个问题:拷贝构造函数,赋值,友元...... 等等

(3). 赋值,拷贝函数应该也要设置成 private, 禁止明显赋值或者拷贝

#include <bits/stdc++.h>

class Gragh {
private:
	int data ;
	// 饿汉式, 线程安全
	static Gragh null ;
public:
	void display () const {
		std::cout << "data = " << data << std::endl ;
	}
	// error: cannot call member function without  object // 要加 static ;非静态成员函数必须要有对象才能调用
	static Gragh &Get_instance () {
		return null ;
	}
private: 
	// 构造函数声明成私有的
	explicit Gragh () : data ( 100 ) {}
	Gragh ( const Gragh &One ) {}              // 拷贝构造函数
	Gragh ( Gragh &One ) {}                    // 拷贝构造函数
	Gragh & operator = ( const Gragh &One ) {  // 赋值操作符
		return null ;
	}
} ;
// undefined reference to null   // 不在类外声明, 会报错
Gragh Gragh::null ;     

int main () {
	Gragh &One = Gragh::Get_instance () ;  // 只能用 classname:: 因为现在还没有对象
	One.display () ;
	std::cout << "One 的地址是  :  " << &One << std::endl ;

	Gragh &Two = Gragh::Get_instance () ;
	Two.display () ;
	std::cout << "Two 的地址是  :  " << &Two << std::endl ;

	// Gragh Three = One ;    // 赋值函数是 private, 错误
	// Gragh Four ( One ) ;   // 拷贝构造函数是 private, 错误
	return 0 ;
}

有一种很流行的方式, #define 宏定义禁止类的构造,拷贝构造函数,赋值操作等等。
有的公司还以此为编码规范
#include <bits/stdc++.h>

class Gragh {

// 宏定义  // 尽量别放在 .h 文件中, 防止污染
#define DISALLOW_COPY_AND_ASSIGN(Gragh)    \
private:                                    \
	Gragh ( Gragh & ) ;                      \
    Gragh ( const Gragh & ) ;                 \
    Gragh & operator = (const Gragh & ) ;      \

private:
	int data ;
	// 饿汉式, 线程安全
	static Gragh null ;
public:
	void display () const {
		std::cout << "data = " << data << std::endl ;
	}
	// error: cannot call member function without  object // 要加 static ;非静态成员函数必须要有对象才能调用
	static Gragh &Get_instance () {
		return null ;
	}
private: 
	// 构造函数声明成私有的
	explicit Gragh () : data ( 100 ) {}
	// 一句话代表三种情况
	DISALLOW_COPY_AND_ASSIGN ( Gragh ) ;
} ;
// undefined reference to null   // 不在类外声明, 会报错
Gragh Gragh::null ;     

int main () {
	Gragh &One = Gragh::Get_instance () ;  // 只能用 classname:: 因为现在还没有对象
	One.display () ;
	std::cout << "One 的地址是  :  " << &One << std::endl ;

	Gragh &Two = Gragh::Get_instance () ;
	Two.display () ;
	std::cout << "Two 的地址是  :  " << &Two << std::endl ;

	// Gragh Three = One ;    // 赋值函数是 private, 错误
	// Gragh Four ( One ) ;   // 拷贝构造函数是 private, 错误
	return 0 ;
}

2018.5.9更:转移构造函数最好也设置为不可用

private:
    Gragh ( Gragh&& One ) ;

// 或者
Gragh ( Gragh&& One ) = delete ;

(4). 利用 C++11 的新特性 = delete 声明

C++11 添加的 =delete 声明,意味着声明了该函数,但是该函数不可以使用!这样就很好地实现了单例模式,无论是构造函数,还是拷贝构造函数,赋值......都可以加上 = delete 声明,禁止使用。
这样一来,单例模式就只能通过 ::Get_instance 来获取这个类的对象,而且有且仅有一个.
#include <bits/stdc++.h>

class Gragh {
private:
	int data ;
	// 饿汉式, 线程安全
	static Gragh null ;
public:
	void display () const {
		std::cout << "data = " << data << std::endl ;
	}
	// error: cannot call member function without  object // 要加 static ;非静态成员函数必须要有对象才能调用
	static Gragh &Get_instance () {
		return null ;
	}
public:
	// 声明为 delete 禁止使用
	// 即使是友元, 声明为 public 也不可以使用
	Gragh ( Gragh & ) = delete ;                
    Gragh ( const Gragh & ) = delete ;            
    Gragh & operator = (const Gragh & ) = delete ;    
private: 
	// 构造函数声明成私有的
	explicit Gragh () : data ( 100 ) {
		std::cout << "null 的构造函数被调用" << std::endl ;
	}
} ;
// undefined reference to null   // 不在类外声明, 会报错
Gragh Gragh::null ;     

int main () {
	Gragh &One = Gragh::Get_instance () ;  // 只能用 classname:: 因为现在还没有对象
	One.display () ;
	std::cout << "One 的地址是  :  " << &One << std::endl ;

	Gragh &Two = Gragh::Get_instance () ;
	Two.display () ;
	std::cout << "Two 的地址是  :  " << &Two << std::endl ;

	// Gragh Three = One ;    // 赋值函数不可使用
	// Gragh Four ( One ) ;   // 拷贝构造函数不可使用
	return 0 ;
}
而且,= delete 声明是针对所有函数,不仅仅是成员函数。只需声明,不需要写函数体。
有的时候,要禁止一些非法参数的调用,比如说, 禁止无参构造函数
Gragh () = delete ;
或者是防止其他参数的干扰,都可以设置成 = delete ;
C++11 还增加了 = default ; 声明,经常用在构造函数和析构函数,意味着要求编译器生成一个构造函数或者析构函数。同样不需要写函数体。

(5). 继承自一个类,禁止拷贝,构造,赋值,以及友元访问。

#include <bits/stdc++.h>

class father {
protected:
	father () = default ;
	~father () = default ;
private:
	father ( father & ) {} 
    father ( const father & ) {}    
    father & operator = ( const father & ) { return *this ; }
} ;

class Gragh : public father {  // 什么继承都会报错, 如果拷贝, 赋值......
private:
	int data ;
	// 饿汉式, 线程安全
	static Gragh null ;
public:
	void display () const {
		std::cout << "data = " << data << std::endl ;
	}
	// error: cannot call member function without  object // 要加 static ;非静态成员函数必须要有对象才能调用
	static Gragh &Get_instance () {
		return null ;
	}
public: 
	// 构造函数声明成公有的
	explicit Gragh () : data ( 100 ) {
		std::cout << "null 的构造函数被调用" << std::endl ;
	}
} ;
// undefined reference to null   // 不在类外声明, 会报错
Gragh Gragh::null ;     

int main () {
	Gragh &One = Gragh::Get_instance () ;  // 只能用 classname:: 因为现在还没有对象
	One.display () ;
	std::cout << "One 的地址是  :  " << &One << std::endl ;

	Gragh &Two = Gragh::Get_instance () ;
	Two.display () ;
	std::cout << "Two 的地址是  :  " << &Two << std::endl ;

	// Gragh Three = One ;    // 赋值函数在父类是私有的
	// Gragh Four ( One ) ;   // 拷贝构造函数在父类是私有的
	return 0 ;
}
可以看出,虽然子类不能继承基类的 private 函数,但是,子类的拷贝构造和赋值,都需要先调用基类的拷贝构造函数或者赋值。

我还是喜欢用指针,因为每次传引用如果少写了 & , 就容易调用赋值,或者拷贝构造函数。
用指针更方便,不用担心写成赋值或者拷贝 但是容易出错。
#include <bits/stdc++.h>

class Gragh {
private:
	int data ;
	static Gragh *null ;
private:
	explicit Gragh () : data ( 100 ) {}
public:
	void display () const {
		std::cout << "data = " << data << std::endl ;
	}
	static Gragh* const Get_instance () {
		return null ;
	}
	static void End_OK () {  // 设置成静态的, 因为不需要对象也可以调用
		if ( null )
			delete null ;
		null = NULL ;
	}
	~Gragh () {}
} ;
Gragh* Gragh::null = new Gragh () ;

int main () {
	Gragh *One = Gragh::Get_instance () ;   
	One->display () ;
	std::cout << "One 的内容是  :  " << One << std::endl ;

	Gragh *Two = Gragh::Get_instance () ;
	Two->display () ;
	std::cout << "Two 的内容是  :  " << One << std::endl ;

	Gragh::End_OK () ;  // 记得释放指针的内存

	return 0 ;
}
运行结果 :

2018.5.9
还可以建立模板,专门产生单例,如果有多个单例,优势很大。但是缺点就是,原先的对象和模板的对象,都可以产生模板的单例,要保证只用其中一个的构造函数。
#include <bits/stdc++.h>
#define rep( i , j , n ) for ( int i = int(j) ; i < int(n) ; ++i )
#define dew( i , j , n ) for ( int i = int(n-1) ; i > int(j) ; --i )
#define _PATH __FILE__ , __LINE__
typedef std::pair < int , int > P ;
using std::cin ;
using std::cout ;
using std::endl ;
using std::string ;

class Gragh {
public:
	int data ;
	int old ;
	Gragh() : data ( 100 ) , old ( 0 ) {}
	~Gragh () {
		cout << endl << "析构函数" << endl ;
	}
	void test_with_member () {
		cout << endl << "我是用了数据成员的普通成员函数" << endl ;
		cout << "data = " << data << endl ;
	}
	void test_no_member () {
		cout << endl << "我是没用数据成员普通的成员函数" << endl ;
	}
	static void test_static () {
		cout << endl << "我是 static 函数" << endl ;
	}
} ;

template< typename T >
class Single {                // 懒汉式单例模板
private:
	static T* null ;
	static std::once_flag flag ;
private:
	explicit Single () = delete ;
	Single ( Single& ) = delete ;
	Single ( const Single& ) = delete ;
	Single ( Single&& ) = delete ;
	Single& operator = ( const Single& ) = delete ;
	Single& operator = ( const Single&& ) = delete ;
public:
	static T* Get_instance () {
		std::call_once ( flag , [&] () { null = new T () ; } ) ;
		return null ;
	}
	static bool End_OK () {
		if ( null ) {
			delete null ; null = nullptr ; return true ;
		} 
		return false ;
	}
} ;
template< typename T >
T* Single<T>::null = nullptr ;  
template< typename T >
std::once_flag Single<T>::flag ;

template< typename T >
class Single2 {                // 饿汉式单例模板
private:
	static T null ;
private:
	explicit Single2 () = delete ;
	Single2 ( Single2& ) = delete ;
	Single2 ( const Single2& ) = delete ;
	Single2 ( Single2&& ) = delete ;
	Single2& operator = ( const Single2& ) = delete ;
	Single2& operator = ( const Single2&& ) = delete ;
public:
	static T* Get_instance () {
		return &null ;
	}
} ;
template< typename T >
T Single2<T>::null ;

int main () {
	Gragh *One = Single2<Gragh>::Get_instance () ;
	cout << One << endl ;
	Gragh *Two = Single2<Gragh>::Get_instance () ;
	cout << Two << endl ;
	return 0 ;
}





猜你喜欢

转载自blog.csdn.net/nishisiyuetian/article/details/80148978