C++ Primer 学习笔记_24_类与数据抽象(10)--static 与单例模式、auto_ptr与单例模式、const成员函数、const 对象、mutable修饰符
前言
【例】写出面向对象的五个基本原则?
解答:单一职责原则,开放封闭原则,依赖倒置原则,接口隔离原则和里氏替换原则
里氏替换原则:子类型必须能够替换他们的基类型。
设计模式分为三种类型:创建型模式、结构型模式和行为型模式
一、static 与单例模式
1、单例模式
单例模式的意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
(1)第一种形式
#include <iostream> #include <string> using namespace std; class Singleton { private: static Singleton s; int i; Singleton(int x): i(x) {cout << "Singleton" << endl;} Singleton & operator = (Singleton&); //不允许赋值 Singleton(const Singleton&); //不允许拷贝 public: static Singleton& instance() {return s;} int getValue() {return i;} void setValue(int x) {i = x;} }; Singleton Singleton::s(47); //定义静态成员s int main() { Singleton& s = Singleton::instance(); cout << s.getValue() << endl; Singleton& s2 = Singleton::instance(); s2.setValue(9); cout << s.getValue() << endl; return 0; }
运行结果:
Singleton
47
9
上述实现通过返回一个引用实现单列模式。如果返回的是一个指针而不是引用,用户可能会不小心删除此指针,因此上述实现比返回指针更安全。
(2)第二种形式
#include <iostream> #include <string> using namespace std; class Singleton { private: int i; Singleton(int x): i(x) {cout << "Singleton" << endl;} void operator=(Singleton&); Singleton(const Singleton&); //不允许拷贝 public: static Singleton& instance() { static Singleton s(47); return s; } int getValue() {return i;} void setValue(int x) {i = x;} }; int main() { Singleton& s = Singleton::instance(); cout << s.getValue() << endl; Singleton& s2 = Singleton::instance(); s2.setValue(9); cout << s.getValue() << endl; return 0; }
运行结果:
Singleton
47
9
上述方法时通过成员函数内部的静态对象的创建实现单例模式。
注意上述两种方法并未考虑线程安全的问题,如需应用在多线程环境下,需要加锁。
【例子:禁止拷贝,赋值,默认构造函数创建对象操作】
#include <iostream> using namespace std; class Singleton { public: static Singleton *GetInstance() { if (instance_ == NULL) { instance_ = new Singleton; } return instance_; } ~Singleton() { cout << "~Singleton ..." << endl; } private: Singleton(const Singleton &other); //将拷贝函数放在private,禁止拷贝 Singleton &operator=(const Singleton &other); //禁止赋值 Singleton() //禁止创建对象 { cout << "Singleton ..." << endl; } static Singleton *instance_; }; Singleton *Singleton::instance_; int main(void) { //Singleton s1; // Error,调用默认构造函数 Singleton *s1 = Singleton::GetInstance(); Singleton *s2 = Singleton::GetInstance(); //Singleton s3(*s1); // Error,调用拷贝构造函数 return 0; }
运行结果:
Singleton ...
上述程序虽然调用了两个GetInstance函数,但只调用一次构造函数,即创建一个对象。将赋值运算符和拷贝构造函数声明为私有,禁止拷贝。但程序存在一个问题就是对象生存期到时不会被析构。
2、为了解决对象不会被析构的问题,可以使用一个静态的嵌套类对象来解决:
#include <iostream> using namespace std; class Singleton { public: static Singleton *GetInstance() { if (instance_ == NULL) { instance_ = new Singleton; } return instance_; } ~Singleton() { cout << "~Singleton ..." << endl; } class Garbo { public: ~Garbo() { if (Singleton::instance_ != NULL) { delete instance_; } } }; private: Singleton(const Singleton &other); //将拷贝函数放在private,禁止拷贝 Singleton &operator=(const Singleton &other); //禁止赋值 Singleton() //禁止创建对象 { cout << "Singleton ..." << endl; } static Singleton* instance_; static Garbo garbo_; // 利用对象的确定性析构 }; Singleton::Garbo Singleton::garbo_; Singleton* Singleton::instance_; int main(void) { //Singleton s1; // Error,调用默认构造函数 Singleton *s1 = Singleton::GetInstance(); Singleton *s2 = Singleton::GetInstance(); //Singleton s3(*s1); // Error,调用拷贝构造函数 return 0; }
运行结果:
Singleton ...
~Singleton ...
解释:利用静态嵌套对象的确定性析构会调用Garbo类的析构函数,在析构函数内delete 单例类的指针。
3、上面办法比较繁琐,也可以返回局部静态对象的引用来解决:
#include <iostream> using namespace std; class Singleton { public: static Singleton& GetInstance() { static Singleton instance; // 局部静态对象 return instance; } ~Singleton() { cout << "~Singleton ..." << endl; } private: Singleton(const Singleton &other); //将拷贝函数放在private,禁止拷贝 Singleton &operator=(const Singleton &other); //禁止赋值 Singleton() //禁止创建对象 { cout << "Singleton ..." << endl; } }; int main(void) { Singleton& s1 = Singleton::GetInstance(); Singleton& s2 = Singleton::GetInstance(); return 0; }
运行结果:
Singleton ...
~Singleton ...
解释:局部静态对象只会初始化一次,所以调用多次GetInstance函数得到的是同一个对象。由于函数内使用了静态对象,故不是线程安全的。
4、实际上也可以使用auto_ptr 智能指针来解决,程序如下,更详细的对auto_ptr将在后续讨论。
#include <iostream> #include<memory> using namespace std; class Singleton { public: static Singleton *GetInstance() { if (instance_.get() == NULL) { instance_ = auto_ptr<Singleton>(new Singleton); } return instance_.get(); } ~Singleton() { cout << "~Singleton ..." << endl; } private: Singleton(const Singleton &other); Singleton &operator=(const Singleton &other); Singleton() { cout << "Singleton ..." << endl; } static auto_ptr<Singleton> instance_; }; auto_ptr<Singleton> Singleton::instance_; int main(void) { Singleton *s1 = Singleton::GetInstance(); Singleton *s2 = Singleton::GetInstance(); return 0; }
运行结果:
Singleton ...
~Singleton ...
5、饿汉式单例模式(在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快)
实际上,上述所有的单例模式例子都不是线程安全的,设想如果两个线程同时运行到语句if (instance
== null),而此时该实例的确没有创建,那么两个线程都会创建一个实例。如果不希望加锁实现线程安全,可以使用饿汉模式(即在main函数之前先生成一个实例):
#include <iostream> using namespace std; class Singleton { public: static const Singleton* GetInstance() { return instance_; } ~Singleton() { cout << "~Singleton ..." << endl; } class Garbo { public: ~Garbo() { if (Singleton::instance_ != NULL) { delete instance_; } } }; private: Singleton(const Singleton &other); //将拷贝函数放在private,禁止拷贝 Singleton &operator=(const Singleton &other); //禁止赋值 Singleton() //禁止创建对象 { cout << "Singleton ..." << endl; } static const Singleton* instance_; static Garbo garbo_; // 利用对象的确定性析构 }; const Singleton* Singleton::instance_ = new Singleton(); Singleton::Garbo Singleton::garbo_; int main(void) { //Singleton s1; // Error,调用默认构造函数 const Singleton *s1 = Singleton::GetInstance(); const Singleton *s2 = Singleton::GetInstance(); //Singleton s3(*s1); // Error,调用拷贝构造函数 return 0; }
运行结果:
Singleton ...
~Singleton ...
6、或者通过加锁方式实现,详细将在后续讨论。
二、const成员函数、const 对象、mutable修饰符
1、const 成员函数
const成员函数不会修改对象的状态
const成员函数只能访问数据成员的值,而不能修改它
2、const 对象
如果把一个对象指定为const,就是告诉编译器不要修改它
const对象的定义:
const 类名 对象名(参数表);
const对象不能调用非const成员函数
3、mutable修饰
用mutable修饰的数据成员即使在const对象或在const成员函数中都可以被修改。
#include <iostream> using namespace std; class Test { public: Test(int x) : x_(x), outputTimes_(0) { } int GetX() const { cout << "const GetX ..." << endl; //x_ = 100; //Error,尝试修改数据成员 return x_; } int GetX() { cout << "GetX ..." << endl; return x_; } void Output() const { cout << "x=" << x_ << endl; outputTimes_++; } int GetOutputTimes() const { return outputTimes_; } private: int x_; mutable int outputTimes_; //mutable修饰 }; int main(void) { const Test t(10); t.GetX(); Test t2(20); t2.GetX(); t.Output(); t.Output(); cout << t.GetOutputTimes() << endl; return 0; }
运行结果:
const GetX ...
GetX ...
x=10
x=10
2
参考:
C++ primer 第四版
Effective C++ 3rd
http://blog.csdn.net/jnu_simba/article/details/9282235
http://blog.csdn.net/zjf280441589/article/details/24704603
C++编程规范
版权声明:本文为博主原创文章,未经博主允许不得转载。