effective C++笔记(二)

来源:互联网 发布:毕业生就业数据 编辑:程序博客网 时间:2024/06/05 23:46

条款4:

确定所有成员对象被初始化。

在C++中如果是内置类型不被初始化是非常危险的行为,这回产生一种不确定性,而自定义类型如果没有被显式初始化则编译器会默认调用其构造函数,而这个构造函数有可能也并非是我们所需要的(以后会讲到)。最好的习惯是在初始化列表中将所有声明的非static成员初始化。

注意:

初始化列表和构造函数大括号中赋值操作是不同的,无论是从行为上还是从概念上都是不一样的。

初始化列表才是真正的初始化,如果是自定义类型则会调用其构造函数并把初始化列表中提供的参数作为实参传入,如果是内置类型则作为初始值初始化。

在大括号中仅仅是赋值操作而已,如果是自定义类型会调用‘=’运算符,如果是内置类型则为赋值操作,并不是初始化操作,这一点从const类型的变量就可以得出结论。

看下这个简单的实例:

#include<iostream>using namespace std;class type{       private:               const int i;       public:              type():i(2){}              void print()              {                   cout<<i<<endl;               }        };int main(){    type t;    t.print();    system("pause");    return 0;}
运行结果如下(DEV C++):

可以看出const变量在初始化列表中得到了概念上的初始化,因为语法上const变量不被初始化时编译器是会报错的。

#include<iostream>
using namespace std;
class type
{
       private:
               const int i;
       public:
              type(){i=2;}
              void print()
              {
                   cout<<i<<endl;
               }        
};
int main()
{
    type t;
    t.print();
    system("pause");
    return 0;
}
如上红色字体中的改变会导致编译器报错。因为这不是初始化,而是赋值操作,此时编译器认为你未给const变量初始化。
以上分析是在概念上分析初始化列表和构造函数大括号中赋值操作的不同
下面从行为上分析两者的不同:
在初始化列表中初始化是发生在调用各自构造函数之前(当有自定义类型成员存在时)。
#include<iostream>#include<list>#include<cstring>using namespace std;class phonenumber{      //...      };class ABEntry{      private:              string theName;              string theAddress;              list<phonenumber> thePhone;              int numConsulted;      public:             ABEntry(const string& name,const string& address,const list<phonenumber>& phones)             :theName(name),             theAddress(address),             thePhone(phones),             numConsulted(0)             {                                      }};
如上程序中当遇到theName(name)时才会调用string的copy构造函数,并且以name作为实参,以此类推。
class ABEntry
{
      private:
              string theName;
              string theAddress;
              list<phonenumber> thePhone;
              int numConsulted;
      public:
             ABEntry(const string& name,const string& address,const list<phonenumber>& phones)
             {
             theName=name;
             theAddress=address;
             thePhone=phones;
             numConsulted=0;     
             }
};
在大括号中如果程序员心中想的是初始化的话就大错特错了,因为这个时候只是在赋值,初始化早已在进入大括号之前就完成,只是现在的赋值操作覆盖掉了初始化后的值而已,这样就导致编译器做了太多的工作,而且还都是无用功,注意这里的内置类型numConsulted在初始化列表和在大括号中效率是一样的,只是我们这里得到一个结论:在初始化列表中将所有的成员都初始化,这样就不用考虑额外的麻烦了,这样岂不更好?
初始化次序:
一句话:按照声明的顺序初始化
条款5:将每个non-local static对象搬到自己的专属函数中,即在该函数中定义该变量为static变量,而且返回其引用
换句话说non-local static对象被local static对象所替代
在单例模式(C++实现)中就是采用这种方式
“gameplayer.h”class type1{private:int num;      public:  type1(int i=0):num(i){}          size_t nums() const{ return num;}             //...};extern type1 tfs;// effective01.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include"gameplayer.h"#include<iostream>using namespace std;class directory{   public:    directory();      //....};directory::directory(){   size_t num=tfs.nums();                   }int main(){    directory t;    return 0;}
此时会发生链接错误,因为在.h文件中的extern type1 tfs对象在.cpp文件中被引用时未必会初始化,这是由extern声明的特性决定的。也就是说在这种情况下初始化的顺序是不确定的。解决方案是通过将non-local static对象改造成local static对象即可。
"gameplayer.h"class type1{private:int num;      public:  type1(int i=0):num(i){}          size_t nums() const{ return num;}             //...};type1& tfs(){static type1 fs;return fs;}// effective01.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include"gameplayer.h"#include<iostream>using namespace std;class directory{   public:    directory();      //....};directory::directory(){   size_t num=tfs().nums();                   }directory& temp(){    static directory td;    return td;}int main(){    directory t=temp();    return 0;} 
如上是不是很像单例模式中使用的函数?

原创粉丝点击