• 【C++】限制某个类所能产生的对象数量


    问题来源:假设系统中只有一台打印机,如何把打印机对象数止限定为一个。

    或者系统只提供16个可分发出去的文件描述符,如何确保文件描述符对象存在的数目不能超过16个。

    在具体的应用过程中,我们可能需要限制某个类所产生的对象数量。

    下面我们先从简单的问题的开始。

    1.如何允许建立零个对象?

      实例化一个对象时,将调用一个构造函数,因而阻止建立某个类的对象,最容易的方法就是把该类的构造函数声明在类的private域。代码示例如下:

    1 class CantBeInstantiated 
    2 {
    3 private:
    4     CantBeInstantiated();
    5     CantBeInstantiated(const CantBeInstantiated&);
    6     ....
    7 };

    2.如何允许建立一个对象?

      将类的构造函数声明为private后,每个用户都没有权力建立对象,但实际应用的需求是我们需要建立一个对象,因此我们需要选择性地放松这个限制。

    对于声明为private的构造函数,我们可以引入友元函数或成员函数来进行访问,并利用静态成员变量来保证对象的唯一,具体实现代码如下:

    //使用友元函数来访问私有构造函数
    namespace PrintingStuff
    {
        class PrintJob;
        class Printer
        {
        public:
            void submitJob(const PrintJob& job);
            void rest();
            void performSelfTest();
            ...
            friend Printer& thePrinter();
        private:
            Printer();
            Printer(const Printer& rhs);
            ...
        };
        
        Printer& thePrinter()
        {
            static Printer p;
            return p;
        }
    }
    using PrintingStuff::thePrinter;
    thePrinter().reset();
    thePrinter().submitJob(buffer);
     1 //使用静态成员函数来访问私有构造函数
     2 class Printer
     3 {
     4 public:
     5     static Printer& thePrinter();
     6     ...
     7 private:
     8     Printer();
     9     Printer(const Printer& rhs);
    10     ...
    11 };
    12 Printer& Printer::thePrinter()
    13 {
    14     static Printer p;
    15     return p;
    16 }
    17 
    18 Printer::thePrinter().reset();
    19 Printer::thePrinter().submitJob(buffer);

    上述两种实现思想是一致的,最终都是通过thePrint()函数来完成客户端与系统打印机交互访问的需求。thePrinter()函数的实现有两点值得注意:

    1)唯一的Printer对象是位于函数里的静态成员而不是在类中的静态成员。在类中的静态对象有两个缺点,一个总是被构造(和释放),即使不使用该对象;另一个缺点是它的初始化时间不确定。

    2)thePrinter()函数没有声明为内联函数,因为内联意味编译器用函数体代替对函数的每一个调用,这样会导致函数内的静态对象在程序内被复制,可能会使程序的静态对象的拷贝超过一个。

    上述两种方法,都解决了允许建立一个对象的问题,但无法解决允许建立多个对象的限制问题。

    3.如何限制允许建立多个对象?

      我们可以换一种思路,不利用友元函数或成员函数来作为建立对象的中介,而引入计数器简单地计算对象的数目,一旦需要太多的对象,就抛出异常。具体代码实现如下:

     1 class Printer 
     2 {
     3 public:
     4     class TooManyObjects{};        //当需要的对象过多时,抛出的异常类
     5     Printer();
     6     ~Printer();
     7     ...
     8 private:
     9     static size_t numObjects;
    10     static size_t maxObjects;
    11     Printer(const Printer& rhs);
    12 };
    13 
    14 size_t Printer::numObjects = 0;
    15 size_t Printer::maxObjects = 10;
    16 Printer::Printer()
    17 {
    18     if(numObjects >= maxObjects)
    19     {
    20         throw TooManyObjects();
    21     }
    22     ...
    23     ++numObjects;
    24 }
    25 Printer::~Printer()
    26 {
    27     ...
    28     --numObjects;
    29 }

      此法的核心思想就是使用numObjects跟踪Printer对象存在的数量。当构造对象时,它的值就增加,释放对象时,它的值就减少。如果试图构造过多的对象,就会抛出一个TooManyObjects类型的异常。通过maxObjects设置可允许建立对象的最大数目。

       以上两种方法基本上可以满足,限制对象数目的要求。但是具体应用中,仍会出现问题,上述两种方法无法解决。如下面代码所示:

    1 class ColorPrinter:public Printer
    2 {
    3     ...
    4 };
    5 
    6 Printer p;
    7 ColorPrinter cp;
     1 class CPFMachine
     2 {
     3 private:
     4     Printer p;
     5     FaxMachine f;
     6     CopyMachine c;
     7     ...
     8 };
     9 CPFMachine m1;
    10 CPFMachine m2;

      利用计数器的方法,上述代码中,都将会产生多个Printer对象,由Printer被作为基类或者被包含于其他类中,导致其他对象的构造时,隐藏构造Printer对象。这主要是计数器方法无法区分对象存在的三种不同环境:只有它们本身;作为其他派生类的基类;被嵌入在更大的对象中

      利用thePrinter()函数的方法,把Printer对象的数量限制为一个,这样做的同时也会让我们每一次运行程序时只能使用一个Printer对象。导致我们不能在程序的不同部分使用不同的Printer对象。如下面伪代码所示:

    1 建立Printer对象p1;
    2 使用p1;
    3 释放p1;
    4 建立Printer对象p2;
    5 使用p2;
    6 释放p2;
    7 ....

    4.如何解决两种方法存在的问题?

      思路:将两种方法结合,将构造函数声明为private,限制其被继承和被包含于其他的类,解决计数器的问题,并提供伪构造函数作为访问对象的接口,并统计对象的个数,来解决限制构造多个对象和程序不同部分使用不同对象的问题。具体代码实现如下:

     1 class Printer
     2 {
     3 public:
     4     class TooManyObjects{};
     5     //伪构造函数
     6     static Printer* makePrinter();
     7     static Printer* makePrinter(const Printer& rhs);
     8     ...
     9 private:
    10     static size_t numObjects;
    11     static const size_t maxObjects;
    12     Printer();
    13     Printer(const Printer& rhs);
    14 };
    15 
    16 size_t Printer::numObjects = 0;
    17 const size_t Printer::maxObjects = 10;
    18 Printer::Printer()
    19 {
    20     if(numObjects >= maxObjects)
    21     {
    22         throw TooManyObjects();
    23     }
    24     ...
    25 }
    26 Printer::Printer(const Printer& rhs)
    27 {
    28     if(numObjects >= maxObjects)
    29     {
    30         throw TooManyObjects();
    31     }
    32     ...
    33 }
    34 Printer* Printer:makePrinter()
    35 {
    36     return new Printer;
    37 }
    38 Printer* Printer::makePrinter(const Printer& rhs)
    39 {
    40     return new Printer(rhs);
    41 }
    42 Printer *p1 = Printer::makePrinter();

      到目前为止,对于限制对象数目的问题,应该是大功告成。但是仍不能满足工程性上的应用,例如我们有大量像Printer需要限制实例数量的类,就必须一遍又一遍地编写一样的代码,每个类编写一次。作为程序员,应该避免这种重复性的工作。

    5.一个具有对象计数功能的基类

      针对重复性工作的问题,解决思路是:构造一个具有实例计数功能的基类,让像Printer这样的类从该基类继承,利用派生类对象的构造,需要先构造基类对象的特点,通过隐藏的基类实现计数功能。具体实现如下:

     1 template<class BeingCounted>
     2 class Counted
     3 {
     4 public:
     5     class TooManyObjects{};
     6     static int objectCount()
     7     {
     8         return numObjects;
     9     }
    10 protected:
    11     Counted();
    12     Counted(const Counted& rhs);
    13     ~Counted() 
    14     {
    15         --numObjects;
    16     }
    17 private:
    18     static int numObjects;
    19     static const size_t maxObjects;
    20     void init();
    21 };
    22 
    23 template<class BeingCounted>
    24 Counted<BeingCounted>::Counted()
    25 {
    26     init();
    27 }
    28 
    29 template<BeingCounted>
    30 Counted<BeingCounted>::Counted(const Counted<BeingCounted>&)
    31 {
    32     init();
    33 }
    34 
    35 template<class BeingCounted>
    36 void Counted<BeingCounted>::init()
    37 {
    38     if(numObjects >= maxObjects)
    39         throw TooManyObjects();
    40     ++numObjects;
    41 }
    42 
    43 class Printer::private Counted<Printer>
    44 {
    45 public:
    46     static Printer* makePrinter();
    47     static Printer* makePrinter(const Printer& rhs);
    48     ~Printer();
    49     void submitJob(const PrintJob& job);
    50     void reset();
    51     void performSelfTest();
    52     ...
    53     using Counted<Printer>::objectCount;
    54     using Counted<Printer>::TooManyObjects;
    55 private:
    56     Printer();
    57     Printer(const Printer& rhs);
    58 };

     

      

  • 相关阅读:
    springmvc跨域+token验证(app后台框架搭建二)
    JSON Web Tokens(JWT)
    spring4+springmvc+mybatis基本框架(app后台框架搭建一)
    [原创] zabbix学习之旅一:源码安装
    ROC 曲线和 AUC 值
    win7 64位系统 Oracle32bit + PL/SQL访问Orale服务,Oracle 11g的安装,中文乱码问题的解决
    CentOS系统安装配置mysql
    Loaded plugins: fastestmirror, refresh-packagekit, security
    求LCA最近公共祖先的离线Tarjan算法_C++
    求LCA最近公共祖先的在线ST算法_C++
  • 原文地址:https://www.cnblogs.com/dwdxdy/p/2651248.html
Copyright © 2020-2023  润新知