您的位置:首页 > Web前端

对象使用之前初始化

2012-10-14 18:53 288 查看
读取未初始化的值会导致不明确的行为。在某些平台上,仅仅只是读取未初始化的值,就可能让你的程序终止运行。更可能的情况是读入一些"半随机"bits ,污染了正在进行读取动作的那个对象,最终导致不可测知的程序行为,以及许多令人不愉快的调试过程。
        永远在使用对前先将它初始化。

一、对于无任何成员的内置类型,你必须手工完成此事。例如:

int x = 0;                                                    //对int 进行手工初始化

const char* text = "A C-style string";     //对指针进行手工初始化

double d;

std::cin>> d;                                             // 以读取input s出am 的方式完成初始化

二、至于内置类型以外的任何其他东西,初始化责任落在构造函数( constructors)身上。规则很简单:确保每一个构造函数都将对象的每一个成员初始化。这个规则很容易奉行,重要的是别混淆了赋值(assignment) 和初始化( initialization)。

        考虑一个用来表现通讯簿的class ,其构造函数如下:

 class PhoneNumber { ... };

class ABEntry {                                                                    //ABEntry ="Address Book Entry"

public:

    ABEntry(const std::string& name, const std::string& address ,const std::list<PhoneNumber>& phones);

private:

std::string theName;

std::string theAddress;

std::list<PhoneNumber> thePhones;

int numTimesConsulted;

ABEntry: :ABEntry(const std: :string& name , const std: : string& address,const std::list<PhoneNumber>& phones)

theName=narne;                                           //这些都是赋值(assignments) ,

theAddress = address;                                //而非初始化(initializations)。

thePhones = phones;

numTimesConsulted = 0;

}

这会导致ABEntry 对象带有你期望(你指定)的值,但不是最佳做法。C++ 规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。

ABEntry 构造函数的一个较佳写法是,使用所谓的member initialization list (成员初值列〉替换赋值动作:

ABEntry: :ABEntry(const std: :string& n缸ne , const std: :string& address,

const std::1工st<PhoneNumber>& phones)

:theNarne(narne) ,

theAddress(address) ,                            //现在,这些都是初始化(initializations )

thePhones(phones) ,

numTimesConsu1ted(0)

{ }                                                         //I现在,构造函数本体不必有任何动作

这个构造函数和上一个的最终结果相同,但通常效率较高。

       如果成员变量是const 或references,它们就一定需要初值,不能被赋值。避免需要记住成员变量何时必须在成员初值列中初始化,何时不需要,最简单的做法就是:总是使用成员初值列。这样做有时候绝对必要,且又往往比赋值更高效。

       而class 的成员变量总是以其声明次序被初始化。回头看看ABEntry. 其theName 成员永远最先被初始化,然后是theAddress. 再来是thePhones. 最后是numTimesConsulted 。即使它们在成员初值列中以不同的次序出现(很不幸那是合法的) ,也不会有任何影响。

三、不同编译单元内定义之non-local static对象"的初始化次序。

      所谓static 对象,其寿命从被构造出来直到程序结束为止,因此stack 和heap-based 对象都被排除。这种对象包括global 对象、定义于namespace 作用域内的对象、在classes 内、在函数内、以及在file 作用域内被声明为static 的对象。函数内的static 对象称为local static 对象(因为它们对函数而言是local) ,其他static对象称为non-local
static 对象。程序结束时static 对象会被自动销毁,也就是它们析构函数会在main ()结束时被自动调用。

     所谓编译单元<translation unit)是指产出单一目标文件(single object file) 的那些源码。基本上它是单一源码文件加上其所含入的头文(#includefiles) .

     现在,我们关心的问题涉及至少两个源码文件,每一个内含至少一个non-local static 对象(也就是说该对象是global 或位于namespace 作用域内,抑或在class 内或file 作用域内被声明为static)。真正的问题是:如果某编译单元内的某个non-local static 对象的初始化动作使用了另一编译单元内的某个non-local static 对象,它所用到的这个对象可能尚未被初始化,因为C++ 对"定义于不同编译单元内的non-local
static 对象"的初始化次序并无明确定义。

例子:

        假设你有一个FileSystem class ,它让互联网上的文件看起来好像位于本机(local)。由于这个class 使世界看起来像个单一文件系统,你

可能会产出一个特殊对象,位于global 或namespace 作用域内,象征单一文件系统:

class FileSystem {                               //来自你的程序库

public:

std::size t numDisks() canst;                    //众多成员函数之一

};

extern FileSystem tfs;                         //预备给客户使用的对象; //tfs 代表"the file system"

现在假设某些客户建立了一个class 用以处理文件系统内的目录(directories) 。恨自然他们的class 会用上theFileSystem 对象:

class Directory {                      //由程序库客户建立

public:

Directory ( params );

} ;

Directory::Directory( params )

std::size t disks = tfs.numDisks();               //使用tfs 对象

 进一步假设,这些客户决定创建一个Directory对象,用来放置临时文件:

Directory tempDir ( params );             //为临时文件而做出的目录

现在,初始化改序的重要性显现出来了:除非tfs 在tempDir 之前先被初始化,否则tempDir 的构造函数会用到尚未初始化的tfs 。但tfs 和ternpDir 是不同的人在不同的时间于不同的源码文件建立起来的,它们是定义于不同编译单元内的non-local static 对象。而你无法确定其初始化顺序。

    一个小小的设计便可完全消除这个问题, 唯一需要做的是:将每个non-local static 对象搬到自己的专属函数内(该对象在此函数内被声明为static) 。这个手法的基础在于:
C++ 保证,函数内的local static 对象会在"该函数被调用期间" "首次遇上该对象之定义式"时被初始化。

以此技术施行于tfs 和tempDir 身上,结果如下:

class FileSystem { ... );             // 同前

FieSystem& tfs ()

{

static FileSystem fs;                //这个函数用来替换tfs 对象:它在FileSystemclass 中可能是个static 。return fs;                               //定义并初始化一个local
static 对象,返回一个reference 指向上述对象

}
class Directory { ... );               // 同前
Directory::Directory( params)
{

// 同前,但原本的reference to tfs 现在改为tfs ()

std::size t disks = tfs() .numDisks( );   

Directory& tempDir()                   //这个函数用来替换tempDir对象,它在Directoryclass 中可能是个static

static Directory td;                       //定义并初始化local static 对象,

return td;                                      //返回→个reference指向上述对象。

}

这么修改之后,这个系统程序的客户完全像以前一样地用它,唯一不同的是他们现在使用tfs ()和tempDir()而不再是tfs 和tempDir。也就是说他们使用函数返回的"指向static 对象"的reference,而不再使用static 对象自身。

请记住:

1、为内置型对象进行手工初始化,因为C++不保证初始化它们。

2、构造函数最好使用成员初值列(member initialization list) ,而不要在构造函数本体内使用赋值操作(assignment) 。初值列列出的成员变         量,其排列次序应该和它们在class 中的声明次序相同。

3、为免除"跨编译单元之初始化次序"问题,请以local static 对象替换non-local static 对象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐