effective c++条款04:确定对象被使用前已先被初始化

版权声明:转载请注明出处,谢谢!!! https://blog.csdn.net/qhdhnbd110/article/details/82999241

1. 总是用成员初始化列初始化成员变量。

int x;
//在某些情况下,x可能被初始化为0,也可能不被初始化,出现随机值
class MyClass
{
private:
    int x;
    int y;
};
...
MyClass p;
//在某些情况下,x,y可能被初始化为0,也可能不被初始化,出现随机值

        虽然C++对于何时会自动初始化有一定的规则,但是规则过于繁琐,所以最佳的解决办法就是任何时候都要手动的初始化对象。

//对于没有成员的内置类型,必须手动初始化
int x = 0;
char Buf[1024] = {0};

 首先不能混淆初始化和赋值。

C++规定:对象的成员变量的初始化发生在进入构造函数本体之前。

注意:这里说的成员变量必须本身是一个有构造函数的对象,对于内置类型的成员变量C++并不保证。

//赋值操作
class MyClass
{
private:
    int SomeValue;
    std::string SomeString;
    std::list<int> SomeList;
public:
    MyClass(const std::string &SomeString, const std::list<int> &SomeList);
};
MyClass::MyClass(const std::string &String, const std::list<int> &List)
{
    //在进入此构造函数之前,成员变量SomeString, SomeList已经被自身的默认构造函数初始化
    //以下仅为赋值操作
    SomeString = String;
    SomeList = List;
    SomeValue = 0;
}

也就是说,在进入构造函数本体之前,本身是对象的成员变量SomeString,SomeList会被自身的default(默认构造函数)函数初始化,在进入构造函数时,就相当于对对象赋值,而不是初始化,那么之前所有对象的默认初始化都浪费了。

改进方法:使用成员初始列替换赋值操作。

//初始化操作
class MyClass
{
private:
    int SomeValue;
    std::string SomeString;
    std::list<int> SomeList;
public:
    MyClass(const std::string &String, const std::list<int> &List);
};
MyClass::MyClass(const std::string &String, const std::list<int> &List)
:SomeString(String), SomeList(List), SomeValue(0)  //这些都是初始化操作
{
    //函数体内为空
}

此时,MyClass构造函数的实参会成为SomeString, SomeList对象中构造函数的实参,进行copy构造,这样就省去调用若干个default构造函数的时间,效率会有所提升。

对于内置类型成员,初始化和赋值成本相同(但如果内置类型为const或者reference,则必须使用初始化而不可赋值),但为了一致性,也在初始化列中初始化。

如果某个类具有多个构造函数,这些构造函数都有自己的成员初始化列,并且这个类中具有多个成员变量,那么就可能带来大量的重复。解决方法是将那些“初始化和赋值成本相同”的成员变量放在一个private的函数中赋值并供所有构造函数调用。

C++的成员初始化顺序是按照成员的声明顺序进行的,而和成员初始化列的顺序无关。

2. 不同编译单元用local static对象替换non-local static对象。

编译单元:指产生单一目标文件的源码。

local static:指函数内的static对象。

non-local static:除了local static都是non-local static对象。

对于不同编译单元的两个non-local static对象,c++是不保证谁会先被初始化的,如果其中一个对象A需要另外一个对象B初始化,而此时B尚未初始化,就会有所麻烦。

//在文件A中
class MyClassA
{
...
public:
    int Fun() const;   //成员函数
...
};
extern MyClassA A;
//在文件B中
class MyClassB
{
...
public:
    MyClassB();
...
};
MyClassB::MyClassB()
{
    int SomeThing = A.Fun(); //调用对象A
}
MyClass B();  //创建一个B类对象

此时初始化次序就先的尤为重要了,如果对象A没有被初始化,直接用来初始化B,程序就会出现意想不到的错误,所以我们需要确定A在B初始化之前已经被初始化。但C++并没有对不同编译单元的两个non-local static对象的初始化次序提供任何保证。

解决方法:将non-local static改成local static

//在文件A中
class MyClassA
{
...
public:
    int Fun() const;   //成员函数
...
};
MyClassA &A()  //用一个函数创建一个local-static对象并且返回其引用
{
    static MyClassA a;
    return a;
}
//在文件B中
class MyClassB
{
...
public:
    MyClassB();
...
};
MyClassB::MyClassB()
{
    int SomeThing = A().Fun(); //调用对象A
}
MyClassB &B()
{
    static  MyClassB b;
    return b;
}

C++保证:函数内的local static对象会在其函数调用期间,首次遇到该对象定义式的时候被初始化;所以在MyClassB中,如果以函数的形式调用对象A,则肯定先会经历一次对象A的初始化,从而避免灾难。

另一个优点是:如果从未调用创建对象的函数,那么还会省去对象构造和析构的成本,而non-local static对象必然会耗费这个成本。

猜你喜欢

转载自blog.csdn.net/qhdhnbd110/article/details/82999241
今日推荐