您的位置:首页 > 编程语言 > C语言/C++

关于对象初始化的一些建议

2014-07-21 13:11 218 查看
        读取未被初始化的值会造成某些不明确的事件,最终导致不可预知的程序行为以及耗费大量时间的调试过程。虽然程序的初始化问题看上去是那些刚接触C++的新手才会犯的错误,但是如果不精心设计它,即便是那些有经验的老手也会在阴沟里翻船,为你的项目带来很糟糕的问题。以下是在Effective C++中提到的关于对象初始化的建议,对于项目很有帮助。

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

        2. 类的构造函数最好使用成员初始化列表,而不是在构造函数内对其进行赋值操作

        对于无任何成员的内置类型,你需要手动对其初始化。

int i = 1;
const char* p = "Hello";
double j;
std::cin >> j;
        但是对于内置类型以外的任何东西,其初始化的责任都落在构造函数的身上,即构造函数确保将对象的每一个成员都初始化。需要注意的是不要搞混了赋值和初始化。

class W;
class test{
public:
test(const std::string, const std::string, const std::list<W>);
private:
std::string a;
std::string b;
std::list<W> c;
int num;
};
test::test(const std::string A, const std::string B, const std::list<W> C): a(A), b(B), c(C), num(0){}{
a = A;  //这些都是赋值
b = B;  //不是初始化
c = C;
num = 0;}


       在C++中,类对象成员的初始化操作发生在进入构造函数本体之前。在test构造函数内,a、b、c都是被赋值,而不是初始化。这些成员的初始化发生在它们的default构造函数被自动调用时,这比进入test构造函数时要早。但是这对num不为真,因为num是内置类型,无法保证在test构造函数被赋值前被初始化。 所以我们使用成员初始化列表来代替赋值操作。
test::test(const std::string A, const std::string B, const std::list<W> C): a(A), b(B), c(C), num(0){}
 
      现在这些都是初始化操作,test构造函数本体不用进行任何操作而且这个版本的效率更高。在上一个赋值版本中,首先调用default构造函数来为a、b、c设立初值,然后再为它们赋新值。而成员初始化列表就避免了这一问题,成员初始化列表的实参都被拿去作为各个成员变量的构造函数的实参,从而提高了效率。本例中,a、b、c分别以A、B、C为初值进行copy构造。对于num来说,其初始化和赋值操作成本相同。比起先调用default函数再调用copy构造函数,单调用一次copy构造函数是非常高效的。当你想要用default构造函数是,也可以使用成员初始化列表,只要制定实参为null即可。
test::test(const std::string A, const std::string B, const std::list<W> C): a(), b(), c(), num(0){}
 
      但是要记得将num显示初始化为0。我们要培养良好的习惯,就是一定要使用成员初始化列表,而且要列出所有的成员变量,这样做会很高效。然而我们也许会面对很多的构造函数,每个构造函数都有自己要初始化的变量,为此我们会做很多重复的工作。为了解决这个问题,我们可以将赋值和初始化成本相同的成员变量移到某个函数中来赋值,再被所有的构造函数调用。

        3. 为避免“跨单元编译之初始化次序”问题,请以local static对象代替non-local static对象

        C++中的成员初始化顺序是固定的,类的成员变量总是按照声明的顺序被初始化。在test类中,a永远是被最先初始化,然后是b,接下来是c,最后是num。即使它们在成员初始化列表以不同的次序出现,也是不影响初始化顺序的。之所以要注意初始化的顺序问题,是为了避免相应的晦涩错误。例如初始化数组是需要指定大小,但要先初始化那个代表大小的变量。如果你已经明确的将成员变量初始化,那么还需要注意的是不同编译单元定义的non-local static对象的初始化顺序。

        在函数内的static对象为local static对象,而global对象、定义在namespace作用域的对象、在class内以及在file作用域内被声明为static的对象为non-local static对象。

        现在我们有两个源码文件,每个文件都至少有一个non-local static对象。如果某个编译单元内的某个non-local static对象的初始化需要另一个编译单元内的non-local static对象,而问题是它所需要的对象可能尚未初始化。下面的例子可以说明问题。

class File{
public:
int size();
......
};
extern File file; //预备给Directory使用的对象
        在Directory类中,我们会用到File类的对象。

class Directory{
public:
Directory(params);
};

Directory::Directory(params){
......
int file_size = file.size();  //使用file对象
......
}

int main(){
......
Directory dir(params);
......
}
        现在问题显露了出来。除非file在dir之前被初始化,否则dir的构造函数会用到尚未初始化的file。但是file以及dir是由不同人在不同时间根据不同的源码文件建立起来的,是定义在不同编译单元的non-local static对象,无法保证file在dir之前被初始化。而C++对于定义在不同编译单元的non-local static对象的初始化顺序没有明确定义。以上的问题很可能给你的程序带来大的麻烦。

        我们仅需一个小小的设计便可以解决这个问题。唯一需要做的是:将每一个non-local static对象搬到其专属函数中,然后这些函数返回一个引用指向其所包含的对象。之后用户调用这些函数,而不直接操作对象。换句话说,就是用local static对象来替代non-local static对象。这个方法的原理在于函数内的local static对象会在函数被调用时、首次遇到对象的定义式时被初始化。所以我们用函数调用替换直接访问non-local static对象,你就可以保证你所获得的引用一定指向一个已经初始化的对象。将此技术运用在以上问题,结果如下:

class File{......};
File& get_file(){  //这个函数用来替代file对象,它可能是File类的static函数
static File file;
return file;
}
class Directory{......};
Directory::Directory(params){
......
int size = get_file().size();
......
}
Directory& get_dir(){ //这个函数用来替代dir对象,它可能是Directory的static函数
static Directory dir;
return dir;
}


        

        
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ 对象 初始化 建议