zoukankan      html  css  js  c++  java
  • More Effective C++ 条款26 限制某个class所能产生的对象数量

    1. 针对某些有特殊功能的类,有时需要限制其对象数量,例如系统中只有一个打印机,因此需要将打印机的对象数目限制为1,或者只有16个file descriptor(文件描述器)可用,因此必须确定不会有更多的descriptor objects被产生出来,在这些情况下.就需要限制对象数目,或者说阻止对象被产生出来.

    2. 允许零个或一个对象

        要限制对象数目,最直接的策略是限制构造函数的调用(以下称策略1),采用这种思想将构造函数设为private,然后声明一个友元函数调用它,并生成一个static对象,像这样:

    class PrintJob;
    class Printer {
    public:
        void submitJob(const PrintJob& job);
        void reset();
        void performSelfTest();
        ...
        friend Printer& thePrinter();
    private:
        Printer();
        Printer(const Printer& rhs);
        ...
    };
    Printer& thePrinter(){
        static Printer p; 
        return p;
    }
    View Code

        当要使用Printer对象时,就调用thePrinter,它返回Printer的引用且保证只产生一个Printer对象,除了将thePrinter声明为friend,还可以使它成为Printer类的static成员,像这样:

    class Printer {
    public:
        static Printer& thePrinter();
        ...
    private:
        Printer();
        Printer(const Printer& rhs);
        ...
    };
    Printer& Printer::thePrinter(){
        static Printer p;
        return p;
    }
    View Code

        thePrinter的实现使用了函数的static对象,与class的static对象相比,函数中的static对象保证在第一次调用该函数时才被初始化,这可以避免由于"C++没有规定定义于不同编译单元内的全局对象的初始化顺序"而导致的错误.

        另外一种策略(以下称策略2)是当外界申请太多对象时,在构造函数内抛出异常:

    class Printer {
    public:
        class TooManyObjects{}; 
        Printer();
        ~Printer();
        ...
    private:
        static size_t numObjects;
        Printer(const Printer& rhs);//由于只允许产生一个对象,所以不允许拷贝
    };
    size_t Printer::numObjects = 0;
    Printer::Printer(){
        if (numObjects >= 1) {
            throw TooManyObjects();
        }
        proceed with normal construction here;
        ++numObjects;
    }
    Printer::~Printer(){
        perform normal destruction here;
        --numObjects;
    }
    View Code

         策略2很容易被一般化,而且可以使对象数目限制在除1以外的值,但在涉及到类的继承以及内含的情况下不起作用,因为"Printer对象可于三种不同状态下生存:(1)它自己,(2)派生物的'base class'成分,(3) 内嵌于较大对象之中",例如:

        涉及继承:

    //彩色打印机类
    class ColorPrinter: public Printer {
        ...
    };
    Printer p;
    ColorPrinter cp; //抛出异常
    View Code

        涉及内含:

    class CPFMachine { 
    private:
        Printer p; 
        FaxMachine f; 
        CopyMachine c;
        ...
    };
    CPFMachine m1; 
    CPFMachine m2; //抛出异常
    View Code

        策略1不存在这个问题,因为将构造函数设为private实际上禁止了继承和内含的发生.由此启发,要禁止类被继承,可以将构造函数设为private,然后通过开放的接口提供对象引用或指针.

        将两种策略结合,使其既有策略1的禁止被继承和内含的特点,又具有策略2的一般化特性,像这样:

    class Printer {
    public:
        class TooManyObjects{};
        static Printer * makePrinter();
        static Printer * makePrinter(const Printer& rhs);
        ...
    private:
        static size_t numObjects;
        static const size_t maxObjects = 10; // 对象的数目限制,也可以使用枚举
        Printer();
        Printer(const Printer& rhs);
    };
    size_t Printer::numObjects = 0;
    const size_t Printer::maxObjects;
    Printer::Printer(){
        if (numObjects >= maxObjects) {
            throw TooManyObjects();
        }
        ...
    }
    Printer::Printer(const Printer& rhs){
        if (numObjects >= maxObjects) {
            throw TooManyObjects();
        }
         ...
    }
    Printer * Printer::makePrinter(){ 
        return new Printer; 
    }
    Printer * Printer::makePrinter(const Printer& rhs){ 
        return new Printer(rhs); 
    }
    View Code

    3. 一个用来计算对象个数的Base Classes

        可以将2中的策略1和策略2的结合一般化,将它抽象为一个类模板,任何需要限制对象数目的类只要继承这个模板的实例化即可:

    template<class BeingCounted>
    class Counted {
    public:
        class TooManyObjects{}; 
        static int objectCount() { return numObjects; }
    protected:
        Counted();
        Counted(const Counted& rhs);
        ~Counted() { --numObjects; }
    private:
        static int numObjects;
        static const size_t maxObjects;
        void init(); 
    }; 
    template<class BeingCounted>
    Counted<BeingCounted>::Counted(){ 
        init(); 
    }
    template<class BeingCounted>
    Counted<BeingCounted>::Counted(const Counted<BeingCounted>&){ 
        init(); 
    }
    template<class BeingCounted>
    void Counted<BeingCounted>::init(){
        if (numObjects >= maxObjects) throw TooManyObjects()
            ++numObjects;
    }
    View Code

        将Counted定义为模板,同一继承层次中的不同类共享同一对象计数,因此通过使用类模板,不同派生类的对象计数得以相互独立.

        Printer要使用Counted就像这样:

    class Printer: private Counted<Printer> {
    public:
        static Printer * makePrinter();
        static Printer * makePrinter(const Printer& rhs);
        ~Printer();
        void submitJob(const PrintJob& job);
        void reset();
        void performSelfTest();
        ...
    using Counted<Printer>::objectCount; 
    using Counted<Printer>::TooManyObjects;
    private:
        Printer();
        Printer(const Printer& rhs);
    };
    View Code

        由于Counted<Printer>是Printer的基类,因此构造Printer之前一定会发生Counted<Printer>的构造,这样就把限制对象个数的任务交由基类Counted<Printer>来完成.此外,由于Printer只继承Counted的实现而不继承接口,因此使用private继承.如果要使开放Counted的部分接口,可以使用using declaration:

    class Printer: private Counted<Printer> {
    public:
        ...
        using Counted<Printer>::objectCount;
        ...
    };
    View Code

        或旧式的访问声明语法:

    class Printer: private Counted<Printer> {
    public:
        ...
        Counted<Printer>::objectCount;
        ...
    };
    View Code

        最后一个需要注意的地方是,static成员的定义问题,由于Counted类模板使用了static成员,因此必须要在类外定义,对于numObjects可以在头文件中初始化为0,但用于限制对象个数的maxObjects只能由用户定义并初始化,如果用户忘记定义,链接时将会报错并提醒.

  • 相关阅读:
    SoftWater——SDN+UnderWater系列论文一
    《面向对象程序设计》2018年春学期寒假及博客作业总结
    2017级面向对象程序设计——团队作业3
    2017级面向对象程序设计——团队作业2
    生活
    一文搞懂transform: skew
    如何实现css渐变圆角边框
    使用腾讯云的图片缩略图服务
    谈谈实现瀑布流布局的几种思路
    vue cli 3 那些事儿
  • 原文地址:https://www.cnblogs.com/reasno/p/4841418.html
Copyright © 2011-2022 走看看