类型转换:
1、 C风格的类型转换不尽如人意:1)很粗鲁,我们可以通过它们在任意类型之间进行转换;2)该类型转换很难进行查找。
2、 C++通过引入4种新的类型转换操作符克服了C风格的类型转换的缺点:static_cast、const_cast、dynamic_cast、reinterpret_cast。有了这些操作符,以前习惯的写法:
(type)expression;
现在通常写成:
static_cast<type>(expression);
3、 static_cast不能去除一个表达式的const属性,应该使用const_cast;const_cast用来去掉一个表达式的const属性或volitile属性。使用const_cast,我们强调的是通过这个转换我们要做的唯一一件事情就是改变某些东西的const属性或volitile属性。这个语义是由编译器来强制约束的。
class Widget {...}
class ACEWidget: public Widget {...}
void update(ACEWidget *psw);
ACEWidget aw;
const ACEWidget& acew = aw;
update(&acew); //Err!不能将const ACEWidget*的实数传给接收ACEWidget*参数的函数
update(const_cast<ACEWidget*>(&acew)); //OK!
update((ACEWidget*)&acew); //与上面的结果一样,不过这里使用的是风格的
widget *pw = new ACEWidget;
update(pw); //Err!pw是Widget*类型,而update函数要求的是ACEWidget*类型
update(const_cast<ACEWidget*>(pw)); //Err!const_cast只能用来除掉表达式的const或volitile
//属性,不能用来进行类继承体系的转换
到目前为止,const_cast最通常的用法就是去除一个对象的const属性。
4、 dynamic_cast是用来针对一个继承体系做向下或者横向的安全转换。即用dynamic_cast把指向基类的指针(或引用)转换成指向派生类或者基类的兄弟类的指针(或引用),而且同时可以知道转换是否成功。空指针(当转换指针的时候)或者异常(当转换引用的时候)意味着转换失败。
widget *pw;
...
update(dynamic_cast<ACEWidget*>(pw)); //正确,传递给update函数的指针
//是指向变量类型为ACEWidget的pw的指针(如果pw确实指向一个对象)
//否则,传递的将是一个空指针
void updateViaRef(ACEWidget& acw);
updateViaRef(dynamic_cast<ACEWidget&>(*pw)); //正确,传递给updateViaRef函数
//一个指向ACEWidget的引用(如果pw确实指向某个对象)
//否则,dynamic_cast将抛出异常
注意,dynamic_cast仅限于操纵继承体系,它不能用于那些没有虚函数的类型,也不能用来去除const属性。
int firstNum, secondNum;
...
double result = dynamic_cast<double>(firstNum)/secondNum; //Err!int没有virtual类型
const ACEWidget acw;
...
update(dynamic_cast<ACEWidget*>(&acw)); //Err!dynamic_cast不能用来去除const属性
5、 使用reinterpret_cast操纵符时,转换结果通常是由编译器的实现定义的,因此,reinterpret_cast几乎是不可移植的。reinterpret_cast最常见的用法是用来在函数指针之间进行类型转换。
对函数指针进行类型转换是不可移植的(C++没有保证所有函数指针都以同一种方式来表示),而且在某些情况下这种转换会产生不正确的结果,因此,应该避免对函数指针进行类型转换,除非迫不得已。
多级指针:
尽管超过两级的多级指针很罕见,但在两种常见的情形下,确实会看到指向指针的指针。
1)第一种情形是当我们声明一个指针数组时:
ACEShape *asha[MAX]; //一个数组,其元素为指向ACEShape的指针
由于数组的名字会退化为指向其首元素的指针,所以指针数组的名字也是一个指向指针的指针:
ACEShape **asha2 = asha;
我们在管理指针缓冲区的类的实现中最常看到这种用法:
template<typename T>
class ACEVector
{
public:
explicit ACEVector(size_t capacity)
: buf(new T* [capacity]), cap(capacity), size(0) {}
...
private:
T **buf; //一个指向数组的指针,该数组元素是指向的指针
size_t cap; //容量
size_t size; //大小
};
....
ACEVector<ACEShape> aces(MAX);
从上面代码实现中可以看出,指向指针的指针可能会很复杂,最好将其隐藏起来。
2)第二种情形是当一个函数需要改变传递给它的指针的值时。如下函数,它将一个指针移动到指向字符串中的下一个字符:
void scanTo(const char **p, char c)
{
while(**p && **p != c)
++*p;
}
传递给scanTo的第一个参数是一个指向指针的指针,该指针值是我们希望改变的,这意味着我们必须传递指针的地址:
char s[] = “Hello World!”;
const char *cp = s;
scanTo(&cp,‘,’); //将cp移动到第一个“,”出现的位置
这种用法在C中是合理地,而在C++中,更习惯、更简单、更安全的做法使使用指向指针的引用作为函数参数,而不是使用指向指针的指针作为参数:
void scanTo(const char *&p, char c)
{
while(*p && *p != c)
++p;
}
....
char s[] = “Hello World!”;
const char *cp = s;
scanTo(cp, “,”);
在C++中,几乎总是首选使用指向指针的引用作为函数参数,而不是指向指针的指针。