很痴迷于对象,但是有时你又想束缚住你的疯狂。例如你在系统中只有一台打印机,所以你想用某种方式把打印机对象数目限定为一个。或者你仅仅取得16个可分发出去的文件描述符,所以应该确保文件描述符对象存在的数目不能超过16个。你如何能够做到这些呢?如何去限制对象的数量呢?

如果这是一个用数学归纳法进行的证明,你会从n=1开始证明,然后从此出发推导出其它证明。幸运的是这既不是一个证明也不是一个归纳。而从n=0开始更具有启发性,所以我们就从这里开始。你如何能够彻底阻止对象实例化(instantiate)呢?

允许建立零个或一个对象

每次实例化一个对象时,我们很确切地知道一件事情:“将调用一个构造函数。”事实确实这样,阻止建立某个类的对象,最容易的方法就是把该类的构造函数声明在类的private域:

class CantBeInstantiated {

private:

CantBeInstantiated();

CantBeInstantiated(const CantBeInstantiated&);

...

};

这样做以后,每个人都没有权力建立对象,我们能够有选择性地放松这个限制。例如如果想为打印机建立类,但是要遵守我们只有一个对象可用的约束,我们应把打印机对象封装在一个函数内,以便让每个人都能访问打印机,但是只有一个打印机对象被建立。:

class PrintJob;                           // forward 声明

// 参见Effective C++条款34

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类的构造函数是private。这样能阻止建立对象。第二、全局函数thePrinter被声明为类的友元,让thePrinter避免私有构造函数引起的限制。最后thePrinter包含一个静态Printer对象,这意味着只有一个对象被建立。

客户端代码无论何时要与系统的打印机进行交互访问,它都要使用thePrinter函数:

class PrintJob {

public:

PrintJob(const string& whatToPrint);

...

};

string buffer;

...                                          //填充buffer

thePrinter().reset();

thePrinter().submitJob(buffer);

当然你感到thePrinter使用全局命名空间完全是多余的。“是的” ,你会说,“全局函数看起来象全局变量,但是全局变量是gauche(笨拙的),我想把所有与打印有关的功能都放到Printer类里。”好的,我绝不敢与使用象gauche这样的词的人争论(Lippman的一贯幽默),这很简单,只要在Prihter类中声明thePrinter为静态函数,然后把它放在你想放的位置。就不再需要友元声明了。使用静态函数,如下所示:

class Printer {

public:

static Printer& thePrinter();

...

private:

Printer();

Printer(const Printer& rhs);

...

};

Printer& Printer::thePrinter()

{

static Printer p;

return p;

}

用户使用printer时有些繁琐:

Printer::thePrinter().reset();

Printer::thePrinter().submitJob(buffer);

另一种方法是把thePrinter移出全局域,放入namespace(命名空间)(参见Effective C++条款28)。命名空间是C++一个较新的特性。任何能在全局域声明东西也能在命名空间里声明。包括类、结构、函数、变量、对象、typedef等等。把它们放入命名空间并不影响它们的行为特性,不过能够防止在不同命名空间里的实体发生命名冲突。把Printer类和thePrinter函数放入一个命名空间,我们就不用担心别人也会使用Printer和thePrinter名字;命名空间能够防止命名冲突。

命名空间从句法上来看有些象类,但是它没有public、protected或private域。所有都是public。如下所示,我们把Printer、thePrinter放入叫做PrintingStuff的命名空间里:

namespace PrintingStuff {

class Printer {                          // 在命名空间

public:                                 // PrintingStuff中的类

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;

}

}

// 命名空间到此结束

使用这个命名空间后,客户端可以通过使用fully-qualified name(完全限制符名)(即包括命名空间的名字),

PrintingStuff::thePrinter().reset();

PrintingStuff::thePrinter().submitJob(buffer);

但是也可以使用using声明,以简化键盘输入:

using PrintingStuff::thePrinter;    // 从命名空间"PrintingStuff"

//引入名字"thePrinter"

// 使其成为当前域

thePrinter().reset();               // 现在可以象使用局部命名

thePrinter().submitJob(buffer);    // 一样,使用thePrinter

在thePrinter的实现上有两个微妙的不引人注目的地方,值得我们看一看。第一,唯一的Pritner对象是位于函数里的静态成员而不是在类中的静态成员,这样做是非常重要的。在类中的静态对象实际上总是被构造(和释放),即使不使用该对象。与此相反,只有第一次执行函数时,才会建立函数中的静态对象,所以如果没有调用函数,就不会建立对象。(不过你得为此付出代价,每次调用函数时都得检查是否需要建立对象。)建立C++一个理论支柱是你不需为你不用的东西而付出,在函数里,把类似于Printer这样的对象定义为静态成员就是坚持这样的理论。你应该尽可能坚持这种理论。

与一个函数的静态成员相比,把Printer声明为类中的静态成员还有一个缺点,它的初始化时间不确定。我们能够准确地知道函数的静态成员什么时候被初始化:“在第一次执行定义静态成员的函数时”。而没有定义一个类的静态成员被初始化的时间。C++为一个translation unit(也就是生成一个object文件的源代码的集合)内的静态成员的初始化顺序提供某种保证,但是对于在不同translation unit中的静态成员的初始化顺序则没有这种保证(参见Effective C++条款47)。在实际使用中,这会给我们带来许多麻烦。当函数的静态成员能够满足我们的需要时,我们就能避免这些麻烦。在这里的例子里,既然它能够满足需要,我们为什么不用它呢?

第二个细微之处是内联与函数内静态对象的关系。再看一下thePrinter的非成员函数形式:

Printer& thePrinter()

{

static Printer p;

return p;

}

除了第一次执行这个函数时(也就是构造p时),其它时候这就是一个一行函数——它由“return p;”一条语句组成。这个函数最适合做为内联函数使用。然而它不能被声明为内联。为什么呢?请想一想,为什么你要把对象声明为静态呢?通常是因为你只想要该对象的一个拷贝。现在再考虑“内联”意味着什么呢?从概念上讲,它意味着编译器用函数体替代该对函数的每一个调用,不过非成员函数还不只这些。非成员函数还有其它的含义。它还意味着internal linkage(内部链接)。

通常情况下,你不需要理解这种语言上令人迷惑的东西,你只需记住一件事:“带有内部链接的函数可能在程序内被复制(也就是说程序的目标(object)代码可能包含一个以上的内部链接函数的代码),这种复制也包括函数内的静态对象。”结果如何?如果建立一个包含局部静态对象的非成员函数,你可能会使程序的静态对象的拷贝超过一个!所以不要建立包含局部静态数据的非成员函数。

但是你可能认为建立函数来返回一个隐藏对象的引用,这种限制对象的数量的方法是错误的。也许你认为只需简单地计算对象的数目,一旦需要太多的对象,就抛出异常,这样做也许会更好。如下所示,这样建立printer对象,:

class Printer {

public:

class TooManyObjects{};                   // 当需要的对象过多时

// 就使用这个异常类

Printer();

~Printer();

...

private:

static size_t numObjects;

Printer(const Printer& rhs);           // 这里只能有一个printer,

// 所以不允许拷贝

};                                  // (参见Effective C++ 条款27)

此法的核心思想就是使用numObjects跟踪Pritner对象存在的数量。当构造类时,它的值就增加,释放类时,它的值就减少。如果试图构造过多的Printer对象,就会抛出一个TooManyObjects类型的异常:

// Obligatory definition of the class static

size_t Printer::numObjects = 0;

Printer::Printer()

{

if (numObjects >= 1) {

throw TooManyObjects();

}

继续运行正常的构造函数;

++numObjects;

}

Printer::~Printer()

{

进行正常的析构函数处理;

--numObjects;

}

这种限制建立对象数目的方法有两个较吸引人的优点。一个是它是直观的,每个人都能理解它的用途。另一个是很容易推广它的用途,可以允许建立对象最多的数量不是一,而是其它大于一的数字。

l         建立对象的环境

这种方法也有一个问题。假设我们一个特殊的打印机,是彩色打印机。这种打印机类有许多地方与普通的打印机类相同,所以我们从普通打印类继承下来:

class ColorPrinter: public Printer {

...

};

现在假设我们系统有一个普通打印机和一个彩色打印机:

Printer p;

ColorPrinter cp;

这两个定义会产生多少Pritner对象?答案是两个:一个是p,一个是cp。在运行时,当构造cp的基类部分时,会抛出TooManyObjects异常。对于许多程序员来说,这可不是他们所期望的事情。(设计时避免从其它的concrete类继承concrete类,就不会遇到这种问题。这种设计思想详见条款M33)。

当其它对象包含Printer对象时,会发生同样的问题:

class CPFMachine {                           // 一种机器,可以复印,打印

private:                                     // 发传真。

Printer p;                                 // 有打印能力

FaxMachine f;                              // 有传真能力

CopyMachine c;                             // 有复印能力

...

};

CPFMachine m1;                        // 运行正常

CPFMachine m2;                         // 抛出 TooManyObjects异常

问题是Printer对象能存在于三种不同的环境中:只有它们本身;作为其它派生类的基类;被嵌入在更大的对象里。存在这些不同环境极大地混淆了跟踪“存在对象的数目”的含义,因为你心目中的“对象的存在” 的含义与编译器不一致。

通常你仅会对允许对象本身存在的情况感兴趣,你希望限制这种实例(instantiation)的数量。如果你使用最初的Printer类示例的方法,就很容易进行这种限制,因为Printer构造函数是private,(不存在friend声明)带有private构造函数的类不能作为基类使用,也不能嵌入到其它对象中。你不能从带有private构造函数的类派生出新类,这个事实导致产生了一种阻止派生类的通用方法,这种方法不需要和限制对象实例数量的方法一起使用。例如,你有一个类FSA,表示一个finite state automata(有限态自动机) 。(这种机器能用于很多环境下,比如用户界面设计),并假设你允许建立任意数量的对象,但是你想禁止从FSA派生出新类。(这样做的一个原因是表明在FSA中存在非虚析构函数。Effective C++ 条款14解释了为什么基类通常需要虚拟析构函数,本书条款M24解释了为什么没有虚函数的类比同等的具有虚函数的类要小。)如下所示,这样设计FSA可以满足你的这两点需求:

class FSA {

public:

// 伪构造函数

static FSA * makeFSA();

static FSA * makeFSA(const FSA& rhs);

...

private:

FSA();

FSA(const FSA& rhs);

...

};

FSA * FSA::makeFSA()

{ return new FSA(); }

FSA * FSA::makeFSA(const FSA& rhs)

{ return new FSA(rhs); }

不象thePrinter函数总是返回一个对象的引用(引用的对象是固定的),每个makeFSA的伪构造函数则是返回一个指向对象的指针(指向的对象都是惟一的,不相同的)。也就是说允许建立的FSA对象数量没有限制。

那好,不过每个伪构造函数都调用new这个事实暗示调用者必须记住调用delete。否则就会发生资源泄漏。如果调用者希望退出生存空间时delete会被自动调用,他可以把makeFSA返回的指针存储在auto_ptr中(参见条款M9);当它们自己退出生存空间时,这种对象能自动地删除它们所指向的对象:

// 间接调用缺省FSA构造函数

auto_ptr<FSA> pfsa1(FSA::makeFSA());

// indirectly call FSA copy constructor

auto_ptr<FSA> pfsa2(FSA::makeFSA(*pfsa1));

...                         // 象通常的指针一样使用pfsa1和pfsa2,

//不过不用操心删除它们。

l         允许对象来去自由

我们知道如何设计只允许建立一个实例的类,我们知道跟踪特定类的对象数量的工作是复杂的,因为在三种不同的环境中都可能调用对象的构造函数,我们知道消除对象计数中混乱现象的方法是把构造函数声明为private。还有最后一点值得我们注意:使用thePrinter函数封装对单个对象的访问,以便把Printer对象的数量限制为一个,这样做的同时也会让我们在每一次运行程序时只能使用一个Printer对象。导致我们不能这样编写代码:

建立 Printer 对象 p1;

使用 p1;

释放 p1;

建立Printer对象p2;

使用 p2;

释放 p2;

...

这种设计在同一时间里没有实例化多个Printer对象,而是在程序的不同部分使用了不同的Printer对象。不允许这样编写有些不合理。毕竟我们没有违反只能存在一个printer的约束。就没有办法使它合法化么?

当然有。我们必须把先前使用的对象计数的代码与刚才看到的伪构造函数代码合并在一起:

class Printer {

public:

class TooManyObjects{};

// 伪构造函数

static Printer * makePrinter();

~Printer();

void submitJob(const PrintJob& job);

void reset();

void performSelfTest();

...

private:

static size_t numObjects;

Printer();

Printer(const Printer& rhs);        //我们不定义这个函数

};                                    //因为不允许

//进行拷贝

// (参见Effective C++条款27)

// Obligatory definition of class static

size_t Printer::numObjects = 0;

Printer::Printer()

{

if (numObjects >= 1) {

throw TooManyObjects();

}

继续运行正常的构造函数;

++numObjects;

}

Printer * Printer::makePrinter()

{ return new Printer; }

当需要的对象过多时,会抛出异常,如果你认为这种方式给你的感觉是unreasonably harsh,你可以让伪构造函数返回一个空指针。当然用户在使用之前应该进行检测。

除了用户必须调用伪构造函数,而不是真正的构造函数之外,它们使用Printer类就象使用其他类一样:

Printer p1;                               // 错误! 缺省构造函数是private

Printer *p2 =

Printer::makePrinter();                   // 正确, 间接调用缺省构造函数

Printer p3 = *p2;                          // 错误! 拷贝构造函数是private

p2->performSelfTest();                   // 所有其它的函数都可以

p2->reset();                             // 正常调用

...

delete p2;                                // 避免内存泄漏,如果

// p2 是一个 auto_ptr,

// 就不需要这步。

这种技术很容易推广到限制对象为任何数量上。我们只需把hard-wired常量值1改为根据某个类而确定的数量,然后消除拷贝对象的约束。例如,下面这个经过修改的Printer类的代码实现,最多允许10个Printer对象存在:

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

};

// Obligatory definitions of class statics

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

如果你的编译器不能编译上述类中Printer::maxObjects的声明,这丝毫也不奇怪。特别是应该做好准备,编译器不能编译把10做为初值赋给这个变量这条语句。给static const成员(例如int, char, enum等等)确定初值的功能是最近才加入到C++中的,所以一些编译器还不允许这样编写。如果没有及时更新你的编译器,可以把maxObjects声明为在一个private内匿名枚举类型里的枚举元素,

class Printer {

private:

enum { maxObjects = 10 };                // 在类中,

...                                      // maxObjects为常量10

};

或者象non-const static成员一样初始化static常量:

class Printer {

private:

static const size_t maxObjects;            // 没有赋给初值

...

};

// 放在一个代码实现的文件中

const size_t Printer::maxObjects = 10;

后面这种方法与原来的方法有一样的效果,但是显示地确定初值能让其他程序员更容易理解。当你的编译器支持在类定义中给const static成员赋初值的功能时,你应该尽可能地利用这个功能。

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

把初始化静态成员撇在一边不说,上述的方法使用起来就像咒语一样灵验,但是另一方面它也有些繁琐。如果我们有大量像Printer需要限制实例数量的类,就必须一遍又一遍地编写一样的代码,每个类编写一次。这将会使大脑变得麻木。应该有一种方法能够自动处理这些事情。难道没有方法把实例计数的思想封装在一个类里吗?

我们很容易地能够编写一个具有实例计数功能的基类,然后让像Printer这样的类从该基类继承,而且我们能做得更好。我们使用一种方法封装全部的计数功能,不但封装维护实例计数器的函数,而且也封装实例计数器本身。(当我们在条款M29中测试引用计数时,将看到需要同样的技术。有关这种设计的测试细节,参见我的文章counting objects。)Printer类的计数器是静态变量numObjects,我们应该把变量放入实例计数类中。然而也需要确保每个进行实例计数的类都有一个相互隔离的计数器。使用计数类模板可以自动生成适当数量的计数器,因为我们能让计数器成为从模板中生成的类的静态成员:

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;

}

从这个模板生成的类仅仅能被做为基类使用,因此构造函数和析构函数被声明为protected。注意private成员函数init用来避免两个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);

};

Printer使用了Counter模板来跟踪存在多少Printer对象,坦率地说,除了Printer的编写者,没有人关心这个事实。它的实现细节最好是private,这就是为什么这里使用private继承的原因(参见Effective C++条款42)。另一种方法是在Printer和counted<Printer>之间使用public继承,但是我们必须给Counted类一个虚拟析构函数。(否则如果有人通过Counted<Printer>*指针删除一个Printer对象,我们就有导致对象行为不正确的风险——参见Effective C++条款14。)条款M24已经说得很明白了,在Counted中存在虚函数,几乎肯定影响从Counted继承下来的对象的大小和布局。我们不想引入这些额外的负担,所以使用private继承来避免这些负担。

Counted所做的大部分工作对于Printer的用户来说都是隐藏的,但是这些用户可能很想知道有当前多少Printer对象存在。Counted模板提供了objectCount函数,用来提供这种信息,但是因为我们使用private继承,这个函数在Printer类中成为了private。为了恢复该函数的public访问权,我们使用using声明:

class Printer: private Counted<Printer> {

public:

...

using Counted<Printer>::objectCount; // 让这个函数对于Printer

//是public

...

};

这样做是合乎语法规则的,但是如果你的编译器不支持命名空间,编译器就不允许这样做。如果这样的话,你应使用老式的访问权声明语法:

class Printer: private Counted<Printer> {

public:

...

Counted<Printer>::objectCount;       // 让objectCount

// 在Printer中是public

...

};

这种更传统的语法与uning声明具有相同的含义。但是我们不赞成这样做。TooManyObjects类应该也应用同样的方式来处理,因为Printer的客户端如果要捕获这种异常类型,它们必须有能力访问TooManyObjects。当Printer继承Counted<Printer>时,它可以忘记有关对象计数的事情。编写Printer类时根本不用考虑对象计数,就好像有其他人会为它计数一样。Printer的构造函数可以是这样的:

Printer::Printer()

{

进行正常的构造函数运行

}

这里有趣的不是你所见到的东西,而是你看不到的东西。不检测对象的数量就好像限制将被超过,执行完构造函数后也不增加存在对象的数目。所有这些现在都由Counted<Printer>的构造函数来处理,因为Counted<Printer>是Printer的基类,我们知道Counted<Printer>的构造函数总在Printer的前面被调用。如果建立过多的对象,Counted<Printer>的构造函数就会抛出异常,甚至都没有调用Printer的构造函数。

最后还有一点需要注意,必须定义Counted内的静态成员。对于numObjects来说,这很容易——我们只需要在Counted的实现文件里定义它即可:

template<class BeingCounted>                 // 定义numObjects

int Counted<BeingCounted>::numObjects;       // 自动把它初始化为0

对于maxObjects来说,则有一些技巧。我们应该把它初始化为什么值呢?如果你想允许建立10个printer对象,我们应该初始化Counted<Printer>::maxObjects为10。另一方面如果我们向允许建立16个文件描述符对象,我们应该初始化Counted<Printer>::maxObjects为16。到底应该怎么做呢?

简单的方法就是什么也不做。我们不对maxObject进行初始化。而是让此类的客户端提供合适的初始化。Printer的作者必须把这条语句加入到一个实现文件里:

const size_t Counted<Printer>::maxObjects = 10;

同样FileDescriptor的作者也得加入这条语句:

const size_t Counted<FileDescriptor>::maxObjects = 16;

如果这些作者忘了对maxObjects进行初始化,会发生什么情况呢?很简单:连接时会发生错误,因为maxObjects没有被定义。如果我们提供了充分的文档对Counted客户端说明了需求,他们会回去加上这个必须的初始化。

More Effective C++之 Item M26:限制某个类所能产生的对象数量相关推荐

  1. More Effective C++之 Item M30:代理类

    虽然你和你的亲家可能住在同一地理位置,但就整个世界而言,通常不是这样的.很不幸,C++还没有认识到这个事实.至少,从它对数组的支持上可以看出一些迹象.在FORTRAN.BASIC甚至是COBOL中,你 ...

  2. More Effective C++之 Item M33:将非尾端类设计为抽象类

    假设你正在从事一个软件项目,它处理动物.在这个软件里,大多数动物能被抽象得非常类似,但两种动物--晰蜴和小鸡--需要特别处理.显然,晰蜴和小鸡与动物类的联系是这样的: 动物类处理所有动物共有的特性,晰 ...

  3. Effective C++ 之 Item 6 : 若不想使用编译器自动生成的函数,就该明确拒绝

    Effective C++ chapter 2. 构造 / 析构 / 赋值运算 (Constructors, Destructors, and Assignment Operators) Item 6 ...

  4. More Effective C++之 Item M35:让自己习惯使用标准C++语言

    自1990年出版以来,<The Annotated C++ Reference Manual >(见原书P285,附录:推荐读物)是最权威的参考手册供程序员们判断什么是C++拥有的而什么不 ...

  5. 【More Effective C++】Item 6

    Item 6:区别Increment和Decrement操作符的前置(prefix)和后置(postfix)形式 前置和后置的区别主要在于函数参数不一样,后置形式中多了一个int型的参数,在后置形式被 ...

  6. More Effective C++之 Item M6:自增(increment)、自减(decrement)操作符前缀形式与后缀形式的区别

    很久以前(八十年代),没有办法区分++和--操作符的前缀与后缀调用.这个问题遭到程序员的报怨,于是C++语言得到了扩展,允许重载increment 和 decrement操作符的两种形式. 然而有一个 ...

  7. Effective Modern C++ Item 27 熟悉依万能引用型别进行重载的替代方案

    Item 27 熟悉依万能引用型别进行重载的替代方案 Item 26说过,万能引用和重载在一起总会产生各种各样的问题,无论是独立函数,成员函数,都最好不要和万能引用放一起重载,其中构造函数和万能引用放 ...

  8. More Effective C++之Item M5:谨慎定义类型转换函数

    C++编译器能够在两种数据类型之间进行隐式转换(implicit conversions),它继承了C语言的转换方法,例如允许把char隐式转换为int和从short隐式转换为double.因此当你把 ...

  9. More Effective C++之 Item M19:理解临时对象的来源

    当程序员之间进行交谈时,他们经常把仅仅需要一小段时间的变量称为临时变量.例如在下面这段swap(交换)例程里: template<class T> void swap(T& obj ...

  10. More Effective C++之Item M19:理解临时对象的来源

    临时对象产生场景: 1.类型隐式转换: 2.函数值返回: 当程序员之间进行交谈时,他们经常把仅仅需要一小段时间的变量称为临时变量.例如在下面这段swap(交换)例程里: template<cla ...

最新文章

  1. JoVE微生物组专刊征稿,写方法拍视频教程发SCI(宏基因组公众号专属福利)
  2. Deep learning前的图像预处理
  3. 第五届上交会欲打通技术贸易“最后一公里”
  4. creating a tensor from a list of numpy.ndarray is extremely slow Please consider converting the lis
  5. DataGrip导入csv数据并把第一行作为列名
  6. 【CSU - 1004】Xi and Bo(并查集,裸题)
  7. plsql连接 oracle instantclient_11_2 本地机器不安装oracle_02
  8. 下载cx oracle 6,在Centos 6上安装cx_Oracle
  9. caffeine 弱引用key的实现
  10. crontab java job_crontab 定时任务
  11. 在Master page 里 CompositeControl 的事件失效了
  12. 【转载】Sqlserver使用Convert函数进行数据类型转换
  13. GB28181移植总结
  14. Xmind 2022精彩体验---什么叫流程图
  15. 广告电商源码--广告电商系统开发源码以及功能分享
  16. 15个常用excel函数公式_(干货)EXCEL常用函数公式大全及举例
  17. div嵌套的div水平垂直居中
  18. 软件测试面试题:所有的软件缺陷都能修复吗?所有的软件缺陷都要修复吗?
  19. 电脑改成,如何把电脑变成无线路由器
  20. 时间管理的十一条黄金定律

热门文章

  1. python基础五之for和while
  2. ugui 转轮_unity3D的FingerGestures小工具
  3. luogu 2184 贪婪大陆
  4. 测试-- 自动化测试selenium(关于API)
  5. cl——long.py
  6. matlab异距分组直方图,如何绘制不等距分组的直方图?
  7. javascript 属性的特性 二十五
  8. 厦门大学计算机科学夏令营过程,厦门大学信息学院计算机系夏令营学生见面会如期召开-厦门大学计算机科学系...
  9. 不同范数下的余弦定理_平行四边形的认识视频
  10. 移动花卡服务器系统异常,开通了抖音移动花卡免流服务,为什么使用抖音不显示免流呢?...