C++学习之五、理解C++疑难问题(三)

2014-11-24 12:34:42 · 作者: · 浏览: 2
声明为const,应该保持其不变。但是有时发现这种情况:函数指定一个const变量,但是接着这个const变量必须传递给一个取非const变量的函数。正确的做法是在程序中保持const的一致性,但是并不能总是这样,尤其是使用第三方的库时更是这样。因此,有时需要用这种类型强制转换。

void g(char *str)

{}

void f(const char *str)

{ g(const_cast(str));}

static_cast:

可以使用static_cast来显示完成C++语言直接支持的转换。

int i = 3;

double result = static_cast(i);

也可以使用static_cast来显示地完成用户定义构造函数或者转换例程所允许的转换。

比如:类A有一个构造函数,这个构造函数取类B的一个对象,那么可以使用static_cast把B对象转换为一个A对象。然而,在需要进行这种转换的大部分情况下,编译器都会自动完成转换。

另一种用法是在继承层次结构中完成向下类型强制转换。

class Base

{

public:

Base(){}

virtual ~Base(){}

};

class Derived:public Base

{

public:

Derived(){}

virtual ~Derived(){}

};

int main()

{

Base *b;

Derived *d = new Derived();

b = d; //会自动向上转换

d = static_cast(b); //需要提供static_cast

Base base;

Derived derived;

Base & br = base;

Derived& dr = static_cast(br);

return 0;

}

这种类型强制转换可以应用于指针,引用,但是不能处理对象本身。static_cast这种转换也不会完成运行时类型检查。

static_cast不能直接把一种类型的指针转换为另一种无关的类型。不能使用static_cast把指针转换为int。不能使用static_cast直接把一种类型的对象转换为另一种对象。不能使用static_cast把一个const类型强制转换为非const类型。任何没有意义的转换,static_cast都做不到。

reinterpret_cast:功能比static_cast强,但安全性更低。

可以把一种类型的指针强制转换为另外一种类型的指针,即使在继承结构它们之间不相关也可以。类似的,可以把一种类型的引用强制转换为另外一种类型的引用,即使这二种引用不相关也可以。还可以把指针转换为int,或者把int转换为指针。使用reinterpret_cast时要格外小心,因为它会把原始的位解释为不同类型,而不完成任何类型检查。

dynamic_cast:使用dynamic_cast进行类型强制转换时,会在继承层次结构中对类型强制转换完成类型检查。可以使用dynamic_cast来对指针或引用进行强制类型转换。dynamic_cast会在运行时检查底层对象的运行时类型信息。如果类型强制转换没有意义,dynamic_cast会返回NULL(对指针转换),或者抛出bad_cast异常(对于引用转换)。

class Base

{

public:

Base(){}

virtual ~Base(){}

};

class Derived:public Base

{

public:

Derived(){}

virtual ~Derived(){}

};

int main()

{

Base *b;

Derived *d = new Derived();

b = d; //会自动向上转换

d = dynamic_cast(b); //需要提供dynamic_cast

Base base;

Derived derived;

Base & br = base;

try{

Derived& dr = dynamic_cast(br);

}catch(bad_cast&){cout<<” bad_cast!”<

return 0;

}

作用域解析操作符:

首先在最内层检查要访问的的名字,然后再逐渐向外,直到全局作用域。不再任何名空间、函数或者类中的名字都在全局作用域中。

有时候,一些作用域中的名字会隐藏其他作用域中同样的名字。

还有时候,你在此作用域中不是想访问默认作用域的此名字,而是想访问别的作用域同样的名字,就要使用作用域解析操作符::,为每个名字限定一个作用域。

注意:全局作用域是未命名的,所以如果要访问全局作用域中的名字,就直接单独使用::,而不需要在前面加上作用域名称。

头文件:头文件中要避免同一个文件的循环引用和多重包含。使用#ifndef机制可以用于避免循环包含和多重包含。

//logger.h

#ifndef __LOGGER__

#define __LOGGER__

#include “Preferences.h”

class Logger{};

#endif //__LOGGER__

要避免头文件的这些问题,另一种做法就是超前引用。

C中实用的工具

1. 变长函数参数列表 如:print()

void debugOut(char *str,...);

...表示任意数量和类型的参数。要访问这些参数,必须使用在中定义的宏。可以声明va_list类型的变量,并通过调用va_start()来初始化该变量。va_stat()的第二个参数必须是参数列表中最右边的命名变量。所有函数都至少需要一个命名参数。在这个参数结束之后,它调用va_end()来结束对变长参数列表的访问。在调用va_start()之后必须调用va_end()来确保函数调用栈最后保持一致状态。

尽量不用此方法:因为不知道参数个数,不知道参数类型。

2. 预处理宏

#define SQUARE(x) ((x)*(x)) //注意预处理宏一定多用小括号

作为经验,尽量不用宏取代内联。很容易出错,不进行类型检查,还可能会带来调试错误(因为你编写的代码不是编译器看到的代码)。



摘自 我和我追逐的梦