Effective C++ 学习笔记(四)

条款26:尽可能延后变量定义式的出现时间

因为变量一旦创建和销毁的时候都会占用资源,所以尽可能延后变量定义式的出现,这样做可增加程序的清晰度并改善程序效率。

条款27:尽量稍作转型动作

(1)c语言的强制类型转换的格式是(T)expressi或者T(expression),c++新式的强制类型转换有const_cast,dynamic_cast,reinterpret_cast和static_cast

C++的四种强制类型转换,所以C++不是类型安全的。分别为:static_cast , dynamic_cast , const_cast , reinterpret_cast,下面的是转自http://www.cnblogs.com/alexqdh/archive/2011/06/09/2075713.html

为什么使用C风格的强制转换可以把想要的任何东西转换成合乎心意的类型。那为什么还需要一个新的C++类型的强制转换呢?

新类型的强制转换可以提供更好的控制强制转换过程,允许控制各种不同种类的强制转换。C++中风格是static_cast<type>(content)。C++风格的强制转换其他的好处是,它们能更清晰的表明它们要干什么。程序员只要扫一眼这样的代码,就能立即知道一个强制转换的目的。

四种转换的区别:

static_cast:可以实现C++中内置基本数据类型之间的相互转换。

1
int c= static_cast < int >(7.987);

如果涉及到类的话,static_cast只能在有相互联系的类型中进行相互转换,不一定包含虚函数

class A
{};
class B: public A
{};
class C
{};
 
int main()
{
     A* a= new A;
     B* b;
     C* c;
     b= static_cast <B>(a);  // 编译不会报错, B类继承A类
     c= static_cast <B>(a);  // 编译报错, C类与A类没有任何关系
     return 1;
}

const_cast: const_cast操作不能在不同的种类间转换。相反,它仅仅把一个它作用的表达式转换成常量。它可以使一个本来不是const类型的数据转换成const类型的,或者把const属性去掉。

reinterpret_cast: 有着和C风格的强制转换同样的能力。它可以转化任何内置的数据类型为其他任何的数据类型,也可以转化任何指针类型为其他的类型。它甚至可以转化内置的数据类型为指针,无须考虑类型安全或者常量的情形。不到万不得已绝对不用。

dynamic_cast: 

(1)其他三种都是编译时完成的,dynamic_cast是运行时处理的,运行时要进行类型检查。

(2)不能用于内置的基本数据类型的强制转换。

(3)dynamic_cast转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回NULL。

(4)使用dynamic_cast进行转换的,基类中一定要有虚函数,否则编译不通过。

        B中需要检测有虚函数的原因:类中存在虚函数,就说明它有想要让基类指针或引用指向派生类对象的情况,此时转换才有意义。

        这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(关于虚函数表的概念,详细可见<Inside c++ object model>)中,

        只有定义了虚函数的类才有虚函数表。

 (5)在类的转换时,在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的。在进行下行转换时,dynamic_cast具有类型检查的功能,比               static_cast更安全。向上转换即为指向子类对象的向下转换,即将父类指针转化子类指针。向下转换的成功与否还与将要转换的类型有关,即要转换的指针指向的对象的实际类型与转换以后的对象类型一定要相同,否则转换失败。

因为dynamic_cast很浪费资源,所以尽量不要使用。

条款28:避免返回handles指向对象内部成分

class Point
{
public:
   Point( int x, int y);
   ....
   void setX( int newVal);
   void setY( int newVal);
};
struct RectData
{
  Point ulhc;
  Point lrhc;
};
class Rectangle
{
 private:
   RectData rect;
 public:
   Point& upperLeft()const { return rect.ulhc;}
   Point& lowerRight()const{ return rect.lrhc;}
};

本来想把RectData里面的point做成private的,但是函数upperLeft函数却返回Point的引用,所以就可以通过upperLeft函数就能返回本想private的成员变量,并且还能够通过返回的reference变量改变该private变量的值。所以可以的出来两个教训:第一:成员变量的封装性最多等于“返回其reference”的函数的访问级别。第二:还可以通过函数返回的reference改变值。

条款29:为“异常安全”而努力是值得的

(1)当异常抛出时要做到不泄露任何资源、不允许数据破坏。意思就是说如果发生异常的话,不能出现资源泄露的情况,不能发生因为异常把数据给破坏掉的情况。

(2)异常安全函数必须提供三个保证之一:第一:基本承诺,即是程序内的任何事物任然保持在有效的状态下。比如说加载图片的一个操作,如果在加载的时候发现不能new一个图片的对象,那么对为该图片提供一个默认的图片。第二:强烈保证:就是一个原子操作,要么可以成功的做成,要么就是什么也不做。第三:不抛掷保证:承诺绝不抛出异常,因为他们总是能够完成他们原先承诺的功能。

强烈保证往往可以使用copy-and-swap实现出来,也就是找一个副本,现在副本上实现成功,然后将副本的内容完全复制给原来的那个对象。这样做好处就是可以改变成功要么不改变,但是会消耗系统资源,因为要重新生成一个对象的副本,并且还要调用拷贝构造函数。

条款30:透彻了解inline的里里外外

(1)inline在编译的时候会将对函数的每一次调用都以函数本体替换,这样会增加目标码大小,会占用编译出来的文件的大小,会造成程序体积太大,在加载程序的时候,有换页行为会降低程序的击中率。inline的申请有两种方法,一种就是在函数前面加一个inline关键字并且函数的实现也一定要在头文件里面实现(和template很像,因为在编译期要进行替换必须要知道这个东西长什么样,所以必须要放在头文件中),要么就是直接在头文件里写了函数的实现体。一定要注意inline函数不要写在CPP文件中,要写在头文件中,否则产生链接错误。inline函数一般要比较小,如果inline函数比较复杂含有循环等复杂的语句,一般编译器不会将该函数设置成inline函数。还有就是构造函数或者析构函数都不能设置成inline函数,因为即使构造函数里面什么也不写,如果在继承的情况下,子类要先调用父类的构造函数,等等,所以看似什么也没有写,其实调用了一大堆东西,所以不适合写成inline函数。

条款31:将文件间的编译依存关系降至最低

你可能感兴趣的:(Effective C++ 学习笔记(四))