设为首页 加入收藏

TOP

singleton pattern--单件模式
2013-01-01 14:40:20 来源: 作者: 【 】 浏览:247
Tags:singleton pattern-- 单件 模式

singleton pattern,又称单件模式,或者单例模式。singleton要求类有且仅有一个实例,并给其他对象提供这一实例。

控制类实例仅有一个,办法有两个:

1.私有化构造函数与copy构造函数,使用一个函数静态变量

 1 #include <iostream> 2class People{ 3private: 4         People(std::string name,int age); 5         People( const People&); 6public: 7         friend People& MadePeople( ); 8public: 9void show( );10private:11         std::string name;12int age;13};1415void People::show( )16{17         std::cout<<"The people's name is "<<name<<std::endl;18         std::cout<<"The people's age is "<<age<<std::endl;19}2021 People& MadePeople( )22{23static People onlyPeople("tom",16);24return onlyPeople; 25}2627int main( int argc,char **argv)28{29    MadePeople( ).show( );30return0;31 }

结果为:

  将对象的构造函数与copy构造函数声明为私有,那么只能通过类的成员函数或者是友元函数来调用构造函数,那么这里我们将只提供一个友元函数来调用该类的私有构造函数,那么如何确保实例化只有一个呢?干脆声明一个静态变量(静态变量这里就不详细深入了,如果有该静态变量,编译器是直接引用的,而不是重新构造一个新的静态变量赋值),而该静态变量获取通过友元函数。  注意这里是函数静态成员不是类的静态成员,类中的静态实际上总是被构造的,即使不使用该对象,但是在函数中的静态成员,如果没有调用该函数,就不会建立该对象,但是每次这个函数被调用时候都会检查是否需要建立对象)。我们应该尽量做到为不需要为我们不用东西付出。而且函数成员的静态变量初始化时间是我们能确定的(第一次调用该函数时候),但是类的成员静态变量是不能确定的。C++(www.cppentry.com)为一个object文件的源代码的集合内静态成员初始化提供某种保证,但是对于不同的源代码集合中静态成员初始化顺序没有这种保证,这些会给我们带来麻烦。例如一个需要初始化的静态变量是根据另外一个静态变量来的,但是有可能这个静态变量初始化顺序还在需要初始化的静态变量之后。
  这里提醒一下如果看过C++(www.cppentry.com)你最好不要做的,你就可能会要说了,不是建议不要写成引用返回值吗?因为现在的情况已经很明确了,只会调用一次来创建People对象的实例。并会赋值到一个People对象。如果在多线程的环境下,该资源就是一个互斥资源,就需要注意使用“锁”来应对相应的问题。所以是可以在函数中建立静态变量。那么通过使用引用从而避免了临时对象的创建的性能消耗。(如果在GCC编译器中,编译器会在返回值创建临时变量这块会做相应的优化)
 
2.私有化copy构造函数,使用类的静态变量。
 1 #include <stdexcept> 2 #include <iostream> 3class TooManyPeople:public std::exception 4{ 5public: 6         TooManyPeople(std::string e) 7        :errorMsg(e) 8        {} 9         ~TooManyPeople( ) throw( ) {}10constchar * what( ) constthrow( );11private:12         std::string errorMsg;13};14constchar* TooManyPeople::what( ) constthrow( )15{16return errorMsg.c_str( );17}18class People{19private20static std::size_t numPeople;21     People(const People&);22public:23     People(std::string name,int age);24     ~People( );25private:26     std::string name;27int age;28 };29 size_t People::numPeople = 0;30 People::People(std::string n,int a:w )31{32if(numPeople > = 1)33    {34throw TooManyPeople("Error:Too Many People");35    }36     name=n;37     age=a;38     ++numPeople;39}4041 People::~People( )42{43    --numPeople;44}4546int main(int argc,char **argv)47{48try49    {50         People p1("tom",17);51         People p2("sam",15);    52    }53catch(std::exception &e)54    {55         std::cerr<<e.what( )<<std::endl;56    }57return0;58 }

运行结果为

这里开放了一个方法来实例化该对象,我们在该方法中加入了逻辑判断,并在类中有一个静态变量来指示该类的实例化数量是多少?当该指示变量大于等于1的时候,我们就封锁该方法,禁止实例化更多的对象,小于的时候就进行实例化,并修改指示变量。所以这里完成了限制实例化个数为1的要求。(这里提示下,如果在多线程下面,指示变量是一个互斥资源)

扩展

现在假设我们规定,环境里只有一个男人和一个女人

class Man:public People{  ... };class Women:Public People{ ... };Man  m;Women w;

这里按我们本意是行的通的,控制每一个类只有一个对象,但是实际上,这里建立Women对象是行不通的。因为构造Man会调用基类构造函数People( ),在构造Women的时候调用构造函数People( ),就会报错。当然当其他类包含People对象时候,会发生同样的问题。

1class Robot2{3private:4        ...5         People p;          //有类似人的能力6        ...7}8Robot h1;9 Robot h2;         //错误

这里被嵌入更大的对象,作为它派生类的基类,这些混淆了存在对象的数目,编程(www.cppentry.com)的本意与编译器不一致。通常我们仅仅对对象本身存在的情况做限制,而不是其他对象。如果采用第一种方式实现单例模式方法,就很容进行这种限制,而且不会影响到其他对象。因为People构造函数是private,带有private构造函数的类不能作为基类使用,也不能嵌入到其他对象中。那么下面一种给我们提供了解决上面因被继承或者嵌入其他对象中造成了混淆的问题思路,如下:

 1class T{ 2public: 3static T * makeT( ); 4static T * makeT( const T& rhs); 5    ... 6private: 7    T( ); 8     T(const T& rhs); 9    ...10};11 T * T::makeT( )12{ 13returnnew T( );14}1516 T* T::makeT( const T& rhs)17{ 18returnnew T(rhs);19 }

那么摆脱上述两种现象问题,在此前代码基础上改正可以得到以下代码:

 1 #include <stdexctpt> 2 #include <iostream> 3 #include <memory> 4class tooManyPeople:public std::exception 5{ 6public: 7         TooManyPeople(std::string e) 8        :errorMsg(e) 9        {}10         ~TooManyPeople( ) throw( ) {}11constchar * what( ) constthrow( );12private:13         std::string errorMsg;14};15constchar* TooManyPeople::what( ) constthrow( )16{17return errorMsg.c_str( );18}19class People{20private21static std::size_t numPeople;22     People(const People&);23     People(std::string name,int age);24public:25static People* MakePeople(std::stirng n,int a);26     ~People( );27private:28     std::string name;29int age;30 };31 size_t People::numPeople = 0;32 People::People(std::string n,int a:w )33{34if(numPeople > = 1)35    {36throw TooManyPeople("Error:Too Many People");37    }38     name=n;39     age=a;40     ++numPeople;41}4243 People* People::MakePeople(std::string n,int a)44{45returnnew People(n,a);46}4748 People::~People( )49{50    --numPeople;51}5253int main(int argc,char **argv)54{55try56    {57         std::auto_ptr<People> p1(People::MakePeople("tom",19));58         std::auto_ptr<People> p2(People::MakePeople("tom",20));59    }60catch(std::exception &e)61    {62         std::cerr<<e.what( )<<std::endl;63    }64return0;65 }

运行结果与上面一致。这里我们控制的实例个数为1个,其实可以稍稍改动一下代码,就可以实现将控制的实例个数为N个了。

 1class People{ 2public: 3class TooManyPeople{ }; 4static People * makePeople( ); 5static People * makePeople( const People& rhs); 6    ... 7private: 8static size_t numPeople; 9staticconstint maxPeople ;10    People( );11     People(const People & rhs);12}13 size_t numPeople = 0;14constint maxPeople=5;15 People* People::makePeople( )16{17returnnew People( );18}19 People* People::makePeople(const People rhs)20{21returnnew People(rhs)22}23People::People( )24{25if(numPeople>=maxPeople)26        {27throw TooManyPeople;28        }29         ...30         ++numPeople;31}32 People::People(const People& rhs)33{34if(numPeople>=maxPeople)35    {36throw TooManyPeople;37    }38    ....39     ++numPeople;40}41 People::~People( )42{43         --numPeople;44 }

那么上面一段代码就可以很好完成控制类的实例化数目了。但是我们还可以进一步扩展,如果代码要求有大量类需要控制类的实例化数目,那么我们需要一遍又一遍的编写上面类似的代码,这样我们不累吗 其实我们可以写一个控制类实例化的模版基类,需要控制实例化的类继承该模版基类即可。

View Code
  1 #include <iostream>  2 #include <stdexcept>  3 #include <memory>  4 template <typename T>  5class tooManyPeople:public std::exception  6{  7public:  8         TooManyPeople(std::string e)  9        :errorMsg(e) 10        {} 11         ~TooManyPeople( ) throw( ) {} 12constchar * what( ) constthrow( ); 13private: 14         std::string errorMsg; 15}; 16 17constchar* TooManyPeople::what( ) constthrow( ) 18{ 19return errorMsg.c_str( ); 20} 21 22 tempalte <typename T>  23class counted 24{ 25public: 26staticint objectCount( ) { return numObject; } 27protected: 28        counted( ); 29         counted(const Counted& rhs); 30         ~Counted( ) { --numObject; } 31private: 32staticconst std::size_t maxObject; 33staticint numObject 34void init( ); 35}; 36 37 template<typename T> int count<T>::numObject=0; 38 39 template<typename T> const std::size_t counted<T>::maxObject=2; 40 41 template<typename T> counted<T>::counted( ) 42{ 43        init( ); 44} 45 46 template<typename T> counted<T>::counted( const counted& rhs) 47{ 48        init( ); 49} 50 51 template<typename T>  void counted<T>::init( ) 52{ 53if(numObject >=maxObject) 54        { 55throw tooManyObject("Error: Too Many Object!!"); 56        } 57         ++numObject; 58} 59 60class People:private Counted<People> 61{ 62public: 63static People * makePeople( ); 64static People * makePeople( const std::string n,constint a); 65static People * makePeople(const People& rhs); 66         ~People( ); 67using Counted<People>::objectCount; 68private: 69        People( ) 70         :name(""),age(0) 71        {} 72         People(const std::string n,constint a) 73        :name(n),age(a) 74        {} 75         People( const People & rhs); 76private: 77         std::string name; 78int age; 79} 80 81 People::People(const People & rhs) 82{ 83     name=rhs.name; 84     age=rhs.age; 85} 86 87 People* People::makePeople( ) 88{ 89returnnew People( ); 90} 91 92 People* People::makePeople( const std::string n,constint a) 93{ 94returnnew People(n,a); 95} 96 97 People* People::makePeople(const People & rhs) 98{ 99returnnew People(rhs);100}101102int main( int argc,char ** argv)103{104try105    {106         std::auto_ptr<People> p1(People::makePeople("tom",16));107         std::cout<<"The number of object is"<<People::counted<People>::objectCount( )<<std::endl;108         std::auto_ptr<People> p2(People::makePeople( ));109         std::cout<<"The number of object is"<<People::counted<People>::objectCount( )<<std::endl;110         std::auto_ptr<People p3(People::makePeople("sim",15));111    }112catch(std::exception &e)113    {114         std::cerr<<e.what( )<<std::endl;115    }116return0;117 }

 运行结果为:

 这里说明一下为什么是私有继承,因为这里使用着是不需要关心这个Counted这个计数基类,所以这里采用私有继承是最好的方式。

 

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇factory mothed pattern--工厂方.. 下一篇临时对象的来源

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: