Effective C++ (二)

 

条款29: 避免返回内部数据的句柄

尽量不要返回类成员变量的地址或引用等句柄,以防止被外部轻易的改变。

而且很容易出问题。如返回了一个类内部成员变量指针指向某个缓冲区,在外部调用delete等操作这个缓冲区,势必造成类内部成员变量不能再有效的使用了。

也不要返回了一个局部对象,局部对象出了作用域之后就被销毁。

 

条款30: 避免这样的成员函数:其返回值是指向成员的非const指针或引用,但成员的访问级比这个函数要低

被返回不仅仅可以是数据成员,还要考虑到成员函数。因为返回一个成员函数的指针也是有可能的,而这个成员函数为私有的时候,通过返回它的指针,外部就可以调用它。

如果你想提高效率而返回成员的指针或引用,那么请加上const。

 

条款31: 千万不要返回局部对象的引用,也不要返回函数内部用new初始化的指针的引用

返回局部对象的引用的话,局部对象有可能随时被销毁。

也不要返回函数内部用new初始化的指针的引用,以防止在外部,用户忘记释放这个空间。

 

条款32: 尽可能地推迟变量的定义

因为在某些时候,还不等不到执行某个变量的定义,函数就返回了。

这样,为了提高效率,在使用对象的地方再定义它。

如:

fun()

{

  somecode;

  if (bVal) return;

  Object a(10);

}

如果bVal为true,则可以避免调用a的构造函数来对a进行初始化,可以提高效率。

 

条款33: 明智地使用内联

内联会导致代码体积膨胀。

虚函数不能为内联。

函数中有循环或递归时,不能用内联。

构造函数和析构函数不适合内联。因为它们里面常常含有很多隐式的代码。比如说,类成员变量A a; 则会在构造函数里面调用它的默认构造函数进行初始化。如果A里面还有其它

 

对象,还会进一步的调用,实际上它的代码体积很大。

如果函数中包含静态对象,通常要避免将它声明为内联函数。

 

条款34: 将文件间的编译依赖性降至最低

很多情况下,改变某个类的定义,会导致很多文件重新被编译。将其定义与声明分离,可能可以解决这种情况。

分离的关键在于,"对类定义的依赖" 被 "对类声明的依赖" 取代了。

只要有可能,尽量让头文件不要依赖于别的文件;如果不可能,就借助于类的声明,不要依靠类的定义。

下面就是这一思想直接深化后的含义:

· 如果可以使用对象的引用和指针,就要避免使用对象本身。定义某个类型的引用和指针只会涉及到这个类型的声明。定义此类型的对象则需要类型定义的参与。

· 尽可能使用类的声明,而不使用类的定义。因为在声明一个函数时,如果用到某个类,是绝对不需要这个类的定义的,即使函数是通过传值来传递和返回这个类

如:

  class Date; // 类的声明

  Date returnADate(); // 正确 ---- 不需要Date的定义

  void takeADate(Date d);

这些情况下只需要知道一个类的声明即可,用不着类的定义。

在头文件中尽量不依赖别的文件,而在cpp中包含。

也可以通过指针,来将对象的实现隐藏起来。

如:

  Person *p; //此时就不用知道Person的定义

工厂模式,也有这些思想在里面。

 

条款35: 使公有继承体现 "是一个" 的含义

公有继承意味着 "是一个" 。当写下类D("Derived" )从类B("Base")公有继承时,你实际上是在告诉编译器(以及读这段代码的人):类型D的每一个对象也是类型B的一个对

 

象。你是在声明:任何可以使用类型B的对象的地方,类型D的对象也可以使用,因为每个类型D的对象是一个类型B的对象。

但要注意,如企鹅是鸟,可以从鸟里面仅有继承下来,但是企鹅不会飞。所以,好的设计是和软件系统现在和将来所要完成的功能密不可分的。

 

条款36: 区分接口继承和实现继承

可以为纯虚函数提供实现,但调用它的唯一方式是通过类名完整地指明是哪个调用。

 

条款37: 决不要重新定义继承而来的非虚函数

因为非虚函数在静态编译的时候就已经确定了,无法实现多态,所以,重写继承来的非虚函数,调用这个非虚函数的时候,只会和指针类型相同。

如: Base *p = new Derive;

p->fun(); //如果fun为非虚的,就会调用 Base::fun(),而不会调用Derive::fun();

 

条款38: 决不要重新定义继承而来的缺省参数值

成员函数有非虚函数和虚函数。根据上个条款可知,非虚函数在派生类中重写一般是错误的。 所以,我们完全可以把讨论的范围缩小为 "继承一个有缺省参数值的虚函数" 的情况。

但是,虚函数是动态绑定而缺省参数值是静态绑定的。

所以,继承而来的缺省参数和父类是相同的,而不受子类指定的默认参数的影响。

如果重写继承而来的缺省参数,这意味着你最终可能调用的是一个定义在派生类,但使用了基类中的缺省参数值的虚函数。

 

条款39: 避免 "向下转换" 继承层次

向下转换难看、容易导致错误,而且使得代码难于理解、升级和维护

不过有时候向下转换还是很有用处的。如:

A是父类

B B2 是子类

C C2 C3 C是B的子类,C2 C3是B2的子类。

fun (A *pa)

{

  if (dynamic_cast <B *> (pa)) {...}  //如果传给fun的是B或B的子类(即C)的指针,这时候就会返回TRUE,就是有效的。

  else if (dynamic_cast <B2 *> (pa)) {...} //如果fun是B2或B2的子类(即C2 C3)的指针,成功。

}

用typeid运算符则达不到这样的效果,它只能具体到某个类,而不能判断是否为它的子类。

 

条款40: 通过分层来体现 "有一个" 或 "用...来实现"

使某个类的对象成为另一个类的数据成员,从而实现将一个类构筑在另一个类之上,这一过程称为 "分层"(Layering)。

"分层" 这一术语有很多同义词,它也常被称为:构成(composition),包含(containment)或嵌入(embedding)。

公有继承的含义是 "是一个"。对应地,分层的含义是 "有一个" 或 "用...来实现"。

 

条款41: 区分继承和模板

类型T影响类的行为吗?如果T不影响行为,你可以使用模板。如果T影响行为,你就需要虚函数,从而要使用继承。

 

条款42: 明智地使用私有继承

如果两个类之间的继承关系为私有,编译器一般不会将派生类对象(如Student)转换成基类对象(如Person)。

私有继承意味着只是继承实现,接口会被忽略。而且调用私有继承的父类的函数,只能用父类名称::函数名()的方式来调用。

类的组合也是只使用某个类的实现,但与私有继承是有区别的,不过一般情况下能使用组合就使用组合。

私有继承意味着 "用...来实现"。如果类D私有继承于类B,类型D的对象只不过是用类型B的对象来实现而已;类型B和类型D的对象之间不存在概念上的关系。

 

条款43: 明智地使用多继承

多继承带来二义性。如两个父类含有相同的成员函数名子的时候。

此时只能通过指定父类名+函数名来调用,但当显式地用一个类名来限制修饰一个虚函数时,函数的行为将不再具有虚拟的特征。

多继承有可能导致钻石结构。

但使用好多继承,避免钻石结构,还是很有用处的。

 

条款44: 说你想说的;理解你所说的

 

条款45: 弄清C++在幕后为你所写、所调用的函数

如果你没声明如下函数,编译器将会为你自动生成:

一个拷贝构造函数,一个赋值运算符,一个析构函数,一对取址运算符。另外,如果你没有声明任何构造函数,它也将为你声明一个缺省构造函数。

注意,生成的析构函数一般是非虚拟的。生成的默认拷贝和赋值构造函数,都是潜拷贝,只按位拷贝。

如果想禁止使用这些函数,显示地将它们声明为private型的。

 

条款46: 宁可编译和链接时出错,也不要运行时出错

 

条款47: 确保非局部静态对象在使用前被初始化

如果在某个被编译单元中,一个对象的初始化要依赖于另一个被编译单元中的另一个对象的值,并且这第二个对象本身也需要初始化,但,第一个对象的初始化可能在第二个对象

 

的初始化之前进行的,这时候就会出问题。

而且,你绝对无法控制不同被编译单元中非局部静态对象的初始化顺序。

使用单件模式(singleton pattern)可以解决这个问题,把每个非局部静态对象转移到函数中,声明它为static。其次,让函数返回这个对象的引用。

原因:C++却明确指出:它们在函数调用过程中初次碰到对象的定义时被初始化。所以,如果你不对非局部静态对象直接访问,而用返回局部静态对象引用的函数调用来代替,就能

 

保证从函数得到的引用指向的是被初始化了的对象。这样做的另一个好处是,如果这个模拟非局部静态对象的函数从没有被调用,也就永远不会带来对象构造和销毁的开销;而对

 

于非局部静态对象来说就没有这样的好事。

 

条款48: 重视编译器警告

只要谈到警告,就要想到警告是和编译器紧密相关的,所以在编程时不要马马虎虎,寄希望于编译器为你找出每一条错误。

 

条款49: 熟悉标准库

 

条款50: 提高对C++的认识

C++的设计目标在于:

· 和C的兼容性。

· 效率。

· 和传统开发工具及环境的兼容性。

· 解决真实问题的可应用性。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值