1️⃣ 允许零个或一个对象

  • 定义
    • 每当即将产生一个对象,我们确知一件事情:会有一个constructor被调用。
    • “阻止某个class产出对象”的最简单方法就是将其<font style="background-color:#FBDE28;">constructors</font>声明为<font style="background-color:#FBDE28;">private</font>

2️⃣ 问题的引入

  • 假设我想为打印机设计一个class,我希望设下“只能存在一台打印机”的约束。

  • 我们可以将”打印机对象“封装在某个函数内,如此一来每个人都能够取用打印机,但只有唯一一个打印机对象会被产生。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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 Printerp; //唯一的一个打印机对象。
    return p;
    }
    • 类的设计思想
      • 第一,Printer classconstructor属性是private,可以压制对象的诞生。
      • 第二,全局函数thePrinter被声明为此class的一个friend,致使thePrinter不受 private constructors的约束。
      • 第三,thePrinter内含一个static Printer对象,意思是只有一个Printer对象会被产生出来。

3️⃣ 值得探讨的地方

第一点

  • 形成唯一一个Printer对象的,是函数中的static对象而非class中的static对象
    • 优点一
      • class拥有一个static对象”的意思是:即使从未被用到,它也会被构造(及析构)
      • “函数拥有一个static对象”的意思是:此对象在函数第一次被调用时才产生
    • 优点二
      • function static的初始化时机:在该函数第一次被调用时,并且在该static被定义处。至于一个class static(或是global static)则不一定在什么时候初始化。
      • C++对于“同一编译单元内的statics”的初始化顺序是有提出一些保证的,但对于“不同编译单元内的statics”的初始化顺序没有任何说明。

第二点

  • 函数的”static对象“与inlining的互动。
    • 为什么不被不将thePrinter申明成inline?
    • inline对于non-member functions意味着这个函数有内部连接(internal linkage)。
      • 函数如果带有内部连接,可能会在程序中被复制,也就是说程序的目标代码(object code)可能会对带有内部连接的函数复制一份以上的代码,而此复制行为也包括函数内的static对象。
      • 结果呢,如果你有一个<font style="background-color:#FBDE28;">inline non-member function</font>并于其中内含一个<font style="background-color:#FBDE28;">local static</font>对象,你的程序可能会拥有多份该<font style="background-color:#FBDE28;">static</font>对象的副本
      • 所以,千万不要产生内含 local static对象的 inline non-member functions

4️⃣ 对象计数

  • 目的

    • 利用numObjects来追踪记录目前存在多少个Printer对象。这个数值将在constructor中累加,并在destructor中递减。如果外界企图构造太多Printer对象,我们就抛出一个类型为 TooManyObjectsexception
  • 案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    class Printer {
    public:
    class TooManyObjects (); //当外界申请太多对象时,
    // 抛出这种 exceptionclass。
    Printer();
    ~Printer();
    ...
    private:
    static size_t numObjects;
    Printer(const Printer&rhs); //有着“打印机个数永远为 1”的限制,
    //所以决不允许复制行为。
    };

    size_t Printer::numObjects = 0;
    Printer::Printer()
    {
    if (numObjects >= 1) {
    throw TooManyObject();
    }
    proceed with normal construction here;
    ++numObjects;
    }

    Printer::~Printer()
    {
    perform normal destruction here;
    --numobjects;
    }