《Effective C++ 中文版 2nd Edition》读后感

    大学的时候学校开了C++的课程,我考的还不错。毕业后就一直搞软件开发,大概一年前开始用VC做项目。最近静下心来看了C++的经典著作《Effective C++ 》,才发现自己的C++水平其实真的很一般!书中提到的有些东西,我竟然从来没有注意过!还有些竟然是我第一次听说!
下面以实例说明(我用我写的更小的例子代替了书中的例子,书中原文我用红色标示)。

1.关于类成员的初始化顺序问题
    条款13的标题是:initialization list中的members初始化次序应该和其在class内的声明次序相同
    我不知道大家在用C++开发的时候有没有注意过这个问题,反正我是从来没有往这方面想过!
    下面来看例子:

class  CMyIntArray
{
public :
    CMyIntArray(
int  lowBound,  int  highBound);
    size_t GetArraySize() 
const  {  return  data.size(); };
private :
    std::vector
< int >  data;
    size_t size;
    
int  lBound, hBound;
};

CMyIntArray::CMyIntArray(
int  lowBound,  int  highBound)
          : size(highBound 
-  lowBound  + 1
          , lBound(lowBound)
          , hBound(highBound)
          , data(size) 
{
}

    如果没有看过这本书,没有任何提示,我想很多搞C++开发的人看不出这简单几行代码有什么问题(当然,编译肯定是不会有问题的)。
    可是,如果程序中有类似于下面的代码,问题就会在程序运行的时候爆发出来:

CMyIntArray oTest( 5 13 );
cout 
<<  oTest.GetArraySize()  <<  endl;

    而且你就算在debug状态下进行跟踪也很难发现问题的根源所在!
    问题处在什么地方呢?就在于成员变量的声明次序上!上面变量声明的次序改为:

size_t size;
int  lBound, hBound;
std::vector
< int >  data;

    问题就可以迎刃而解!
    惊奇吗?为什么会这样?书中给出了答案:class members系以它们在class内的声明次序来初始化;和它们在member initialization list中出现的次序完全无关
    上面代码的错误根源是:data在初始化的时候size还没有定义!也就是说要初始化一个没有定义大小的vector,当然会出错。

2.关于“切割问题”
     下面是书中的一段话(条款22中):
    当一个derived class object被交出去当作一个base class object时,它原本所以“成为一个derived class object”的所有特征,都会被切除(slicing)掉,只留下内部一个base class object
     下面是我写的小例子:

class  CBase
{
public :
    
virtual   void  Test()  const  { cout  <<   " Output from CBase! "   <<  endl; };
};

class  CDerived :  public  CBase
{
public :
    
virtual   void  Test()  const  { cout  <<   " Output from CDerived! "   <<  endl; };
};

    下面是两个函数:

void  Test1(CBase test)
{
    test.Test();
}

void  Test2( const  CBase &  test)
{
    test.Test();
}

    用下面的代码分别调用函数Test1和Test2:

CDerived oTest;
Test1(oTest);
Test2(oTest);

    问题是:调用函数Test1和函数Test2分别输出什么?
    正确答案是:

Output from CBase!
Output from CDerived!

    惊奇吗?一点儿也不惊奇!书中已经给出了为什么会这样:以by reference的方式传递参数,有另一个优点:可避免所谓的“切割(slicing)问题”

3.关于非虚拟函数的静态绑定和虚拟函数的动态绑定
    下面是例子:

class  CBase
{
public :
    
virtual   void  Test()  const  { cout  <<   " Output from CBase! "   <<  endl; };
};

class  CDerived :  public  CBase
{
public :
    
void  Test()  const  { cout  <<   " Output from CDerived! "   <<  endl; };
};

    现在有下面的代码调用:

CDerived  d;

CBase 
* pB  =   & d;
pB
-> Test();

CDerived  
* pD  =   & d;
pD
-> Test();

    输出是:

Output from CDerived!
Output from CDerived!

    这个大家应该都知道为什么。可是,如果把CBase的Test方法前的virtual去掉,结果就变了!输出就是:

Output from CBase!
Output from CDerived!

    这时,有些人就会奇怪了,pB和pD指向的都是d,为什么会这样输出呢?
    答案就是:非虚拟函数是静态绑定的,虚拟函数是动态绑定的。
    书中的条款37给了大家忠告:绝对不要重新定义继承而来的非虚拟函数

4.关于缺省值的静态绑定
    还是看例子:

class  CBase
{
public :
    
virtual   void  Test( int  iTest  =   0 const    =   0 ;
};

class  CDerived :  public  CBase
{
public :
    
void  Test( int  iTest  =   1 const  { cout  <<  iTest  <<  endl; };
};

    下面是调用代码:

CBase  * =   new  CDerived;
p
-> Test();

    我们的本意是想输出1,可是结果却是输出0!
    不用惊奇,书中给出了答案:虚拟函数系动态绑定(dynamically bound),而缺省参数值却是静态绑定(statically bound)
    而且,书中的条款38也给了大家忠告:绝对不要重新定义继承而来的缺省参数

    写在最后:其实VC和BCB之类的只能是C++的开发工具,而基础还是C++。就算你的VC和BCB用得再熟练,也是仅仅是对其提供的类库而已,而没有真正领会C++的精髓。这些是我看了这本书后的感想。现在觉得静下心来看了这本书,真是受益匪浅呀。希望这篇文章能给读者一点启发。我的下一个目标就是读C++之父Bjarne Stroustrup的那本经典著作《The C++ Programming Language》。如果我有什么收获,也会在这里给大家分享。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值