《C++高级编程》之理解C++疑难问题


C++疑难问题学习

一、引用
1、引用
   C++引用是另外一个变量的别名(alias)。对引用的所有修改都会改变该引用所指向变量值。可以把引用看成是一种隐式指针,它可以免除获取变量地址和对指针解除引用的麻烦。
①、引用变量
   引用变量必须在创建时就初始化。
   [必须在分配引用时对其初始化,通常,引用是在声明时分配的,不过引用数据成员可以在包含该成员的类的初始化列表中进行初始化。]
   除非引用指向一个const值,否则不能创建指向未命名值得引用。
   int& unnamedRef = 5; // does not compile...
   const int& unnamedRef = 5;//OK
②、修改引用
   引用指向的变量在初始化之后不能再改变,只能改变这个变量的值。
③、指针引用和引用指针
   可以创建指向任何类型的引用,包括指针类型。
   例:int* intP;
       int*& ptrRef = intP;
       ptrRef = new int;
       *ptrRef = 5;
注:不能声明指向引用的引用,也不能声明引用指针(指向引用的指针)。

2、引用数据成员
   类的数据成员可以是引用,必须在构造函数初始化列表中初始化引用数据成员,而不是在构造函数体中完成初始化。

3、引用参数
   C++程序员不常使用独立的引用变量或者引用数据成员,最通常的用法是作为函数和方法的参数。
①、来自指针的引用
例:int x = 5, y = 6;
      int *xp = &x, *yp = &y;
      swap(*xp, *yp); //接收引用参数
②、传引用和传值
   传引用可以避免复制函数实参,在某些情况下会带来两个好处:
   I、效率:对象和结构体的复制会花费很长时间。
   II、正确性:不是所有的对象都允许传值,即使允许传值,也不见得就能正确的支持深复制。

4、 引用返回类型
   从函数或方法也可以返回引用(效率考虑),不是返回一个完整的对象,而是从函数或方法返回对象引用,可以避免不必要的复制。
   [不要返回变量(Stack自动分配)的引用,因为函数结束时会销毁这个变量]

5、 采用引用还是指针
   唯一需要使用指针的情况是,需要改变指针指向的位置,因为不能改变引用指向的变量,比如,在动态分配内存时,就需要在指针中存储指向结果的指针,而不是在引用中存储。

二、关键字疑点
1、const关键字(用于标示变量和用于标示方法)
   ①、const变量
     其中一个用法是代替#define来声明常量,可以标示任何变量,包括全局变量和类数据成员。
   ②、const int* p; //不能改变p指向的值。
       int* const p; //把p自己声明为const。
       [int const* const p = NULL;] == [const int* const p = NULL;] //指针及其指向的值都是const。
   ③、const方法
     声明方法可以防止方法修改类中不可变的数据成员。

2、static关键字
    ①、static数据成员和方法
      可以为类声明静态数据成员和静态方法。
    ②、static连接
       C++连接概念:
         外部连接是指,对于其他源文件,这个名字是可用的。
         内部连接(静态连接)是指,对于其他源文件,这个名字是不可用的。
         函数和全局变量默认都有外部连接,可以在声明前加static,来指定内部连接。
3、extern关键字
   extern用来为位于它前面的名字声明外部连接,
4、函数中的static变量
   静态变量的一种用法是“记住”是否已经为一个函数完成过特定的初始化。

三、类型与类型强制转换
1、typedef
   typedef为既有的类型提一个新的名字。typedef没有创建新类型—它只是提供了引用原类型的新方法。
   类型名最常见的用法是当实际的类型名很有麻烦时可以提供一个可管理的名字,这种情况通常会在模板中发生。(STL大量使用了typedef来定义较短类型名称)
   例:typedef basic_string<char> string;
   typedef的一个巧妙之处是:类型名可以包含作用域限定符。
   例:typedef A::B C;

2、类型强制转换
   C中使用()进行强制类型转换,这种老式的转换方法在C++仍然可用的。但是C++提供了四种新的类型转换方法:static_cast,dynamic_cast,const_cast和reinterpret_cast.因为C++的类型强制转换会完成更多的类型检查,这样可以得到更好的代码。
①、const_cast
    const_cast是最直接的类型强制转换。使用const_cast可以去除变量的常量性。
     例:const_cast<char*>(str) //template

②、static_cast
◇   可以使用static_cast来显示地完成C++语言直接支持的转换。
   例:double result = static_cast<double>(i);
◇   static_cast的另一个用法是在继承层次结构中完成向下类型强制转换。
例:Base* b; Derived* d = new Derived(); à d = static_cast<Derived*>b;
    Base b; Derived d; Base& br = b; Derived& dr = static_cast<Derived&>b;
这些类型强制转换可以应用于指针和引用,但是不能处理对象本身。
注:使用static_cast类型强制转换,不会完成运行时类型检查。要想安全地完成类型强制转换,同时进行运行时类型检查,请使用dynamic_cast。
根据C++类型规定,任何一种没有意义的转换static_cast都做不到。

③、reinterpret_cast
   reinterpret_cast功能比static_cast更强,随之而来的是安全性较低。
   例:calss X; calss Y; X* xp; Y* yp; à xp = reinterpret_cast<X*>(yp);
   在使用reinterpret_cast时要格外小心,因为它会把原来的位重新解释为不同的类型,而不完成任何类型检查。

④、dynamic_cast
   可以使用dynamic_cast来对指针或引用进行类型强制转换,dynamic_cast会在运行时检查底层对象的运行时类型信息。如果类型强制转换没有意义,dynamic_cast会返回NULL(对于指针转换)或者bad_cast异常(对于引用转换)。
    注意:运行时类型信息是存储在对象的vtable里面的,因此,要使用dynamic_cast,类必须至少有一个虚函数。
    需要注意的是,可以使用static_cast或reinterpret_cast沿着继承层次结构完成同样的向下类型强制转换。他们与dynamci_cast的区别是,dynamic_cast要完成运行时(动态)类型检查。

3、解析作用域
如果不想让一个变量使用默认的作用域解析,可以使用作用域解析操作符::。
4、头文件
超前引用:类头文件中声明class preference;而非#include包含。
5、C实用工具
   C++是C的超集,但是C的几个隐含特性在C++中却没有对应的替代品。
   如变长参数列表和预处理宏。

相关内容