• MoreEffectiveC++Item35 条款26: 限制某个class所能产生的对象个数


    一 允许零个或一个对象

    我们知道每当即将产生一个对象,我们有一个constructor被调用,那么我们现在想组织某个对象的产生,最简单的方法就是将其构造函数声明成private(这样做同事防止了这个类被继承)

    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;
    }

    当要使用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;
    }

    看到这里我们可能会想到,创建一个类,然后类中声明一个static Printer对象,用一个函数返回它也可以呀,答案是不行

    缺点1:class中包含一个static对象,和函数中拥有一个static对象是由差别的,calass中拥有一个static对象的意思是,无论你是否用到过这个对象,它都会被构造(及析构).

    相反函数中拥有一个static对象,只有在第一次调用此函数时,这个对象才会产生,如果函数从未被调用,这个对象就不会诞生(然而你必须付出,每次调用函数时都会检查对象是否要诞生)

    缺点2:调用的时机,class static的初始化时机我们无法控制,而function static可以.

    下面我们来介绍一种控制类创建个数的方法

    class Printer {
    public:
        class TooManyObjects{}; //当外界申请太多对象时抛出这种exception class
        Printer();
        ~Printer();
        ...
    private:
        static size_t numObjects;
        Printer(const Printer& rhs);//由于只允许产生一个对象,所以不允许拷贝
    };
    size_t Printer::numObjects = 0;// static做成员变量 必须在class外初始化
    Printer::Printer(){
        if (numObjects >= 1) {
            throw TooManyObjects();
        }
        proceed with normal construction here;
        ++numObjects;
    }
    Printer::~Printer(){
        perform normal destruction here;
        --numObjects;
    }

    二 不同对象的构造状态

    现在我们创建一个带彩印的打印机,这种打印机类有许多地方与普通的打印机类相同,所以我们从普通打印类继承下来

    class ColorPrinter: public Printer {
    ...
    };
    
    //这时我们创建一个打印机和一个彩印机
    Printer p;
    ColorPrinter cp;

    这两个定义会产生多少 Pritner 对象?答案是两个:一个是 p,一个是 cp。在运行时,当构造 cp 的基类部分时,会抛出 TooManyObjects 异常

    如果class CPFMachine 中含有class Printer 那么我们创建两个class CPFMachine 时也会抛出 TooManyObjects 异常

    class CPFMachine { // 一种机器,可以复印,打印
    private: // 发传真。
    Printer p; // 有打印能力
    FaxMachine f; // 有传真能力
    CopyMachine c; // 有复印能力
    ...
    };
    CPFMachine m1; // 运行正常
    CPFMachine m2; // 抛出 TooManyObjects 异常

    问题出在Printer对象可与3中不同状态下存在

    1.它自己

    2.派生类的base class成分

    3.内嵌于较大的对象之中

    现在假设你想创建一个可以产生任意数量的类,且你不希望任何类继承自它

    class FSA {
        public:
        // 伪构造函数
        static FSA * makeFSA();
        static FSA * makeFSA(const FSA& rhs);
        ...
    
        private:
        FSA();
        FSA(const FSA& rhs);
      ...
       auto_ptr<FSA> pfsa1(FSA::makeFSA());//因为调用new,所以我们必须得delete,使用auto_ptr就不用考虑delete了
       auto_ptr<FSA> pfsa2(FSA::makeFSA(*pfsa1));
    
        ...
    };

    三 一个用来计数对象的Base Class

    可以将上面的方法结合一般化,将他抽象一个类模板,任何需要限制对象数目的类只要继承自这个类模板即可

    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;
    }

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

    从这个模板生成的类仅仅能被做为基类使用,因此构造函数和析构函数被声明为protected。注意 private 成员函数 init 用来避免两个 Counted 构造函数的语句重复

    现在我们更改Printer class让她运用Counted template

    class Printer: private Counted<Printer> {//注意我们这里采用私有继承,可以不必声明虚析构函数.那么这个时候我们使用Counted<Printer>删除一一个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; //让此函数对于printer的用户而言成为public
    using Counted<Printer>::TooManyObjects;
    private:
        Printer();
        Printer(const Printer& rhs);
    };

    现在所 有 这 些 现 在 都 由Counted<Printer>的构造函数来处理,因为 Counted<Printer>是 Printer 的基类,我们知道 Counted<Printer>的构造函数总在 Printer 的前面被调用。如果建立过多的对象,Counted<Printer>的构造函数就会抛出异常,甚至都没有调用 Printer 的构造函数.

    如果想要限制生成对象的个数则需在

    //增加类模板实现文件
    template<class BeingCounted> // 定义 numObjects
    int Counted<BeingCounted>::numObjects; // 自动把它初始化为 0

    如果Printer想要使用加限制生成对象数量的类模板则需在Printer加这个实现

    const size_t Counted<Printer>::maxObjects = 10;
  • 相关阅读:
    Spring配置文件中的那些标签意味着什么(持续更新)
    转 spring配置文件
    Web.xml配置详解之context-param
    web.xml 中的listener、 filter、servlet 加载顺序及其详解
    spring mvc 中web.xml配置信息解释
    转 一个web项目web.xml的配置中<context-param>配置作用
    在web.xml中通过contextConfigLocation配置spring
    (转)web.xml中的contextConfigLocation在spring中的作用
    Android菜鸟的成长笔记(20)——IntentService
    PhotoSwipe源码解读系列(二)
  • 原文地址:https://www.cnblogs.com/LearningTheLoad/p/6947737.html
Copyright © 2020-2023  润新知