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

2014-11-24 12:34:42 · 作者: · 浏览: 1
对其初始化。

对于:

const int * const p = &x;// 既不能通过指针p来修改x的值, 也不能修改p指向的对象。

b.const 引用:

应用于引用的const关键字通常比应用于指针const关键字要简单。原因有二,一:引用默认就是const的,也就是说不能修改它们指示的变量(即不能让它再指示别的变量)。所以,C++不允许显式地用const来标识引用变量(即如,int & const xRef = x)。二:引用一般只是一个间接层。不能创建对引用的引用。要得到多重间接层(间接引用),唯一的办法就是创建指针的引用。

因此,我们谈到的const引用,其实是指:

int z;

const int &zRef = z; 等价于int const &zRef = z;

zRef = 4; //error

z = 4;//ok

const引用最常见就是作为函数或方法的参数。

注意:把对象作为参数传递时,默认的做法应该是传递const引用。只有确实需要改变传递过来的对象时才应该去掉const。

c.const方法

用来声明方法不能修改类中不可变的数据成员。

2. 关键字static

C++中的static关键字有三种,而且看起来不相关的用法。

a.static数据成员和方法

它们不属于某个对象,而是属于这个类。

b.static连接

C++每个源文件都是独立编译的,得到的对象文件要连接在一起。C++源文件中的每个名字,包括函数和全局变量,都有一个连接,可能是内部(internal)连接,也可能是外部(external)连接。外部连接是指,对于其他源文件,这个名字是可用的。内部连接(也称为静态连接(static linkage))是指,对于其他源文件,这个名字不可用。函数和全局变量默认都有外部连接。但是,可以在声明前面加上关键字static,来指定内部(静态)连接。

//FirstFile.cpp

void f();

int main()

{

f();

return 0;

}

给出了f()的原型,但没有定义

// AntherFile.cpp

#include

using namespace std;

void f();

void f()

{

cout<<”f\n”<

}

给出了f()的原型和定义。

需要说明的是,在二个不同的文件中编写同一个函数的原型是合法的。如果每个源文件都用#include包含了一个头文件,并把方法的原型放在这个头文件中,预处理所做的正是这个工作,其作用就是在不同的源文件中有同一个方法的原型。使用头文件的原因是维护原型的副本(并保持同步更新)更为容易。不过,对于这个例子没有使用头文件。

这些文件编译与连接都能通过,因为f()有外部连接,main()函数可以从不同的文件调用它。

然而,如果在AntherFile.cpp文件中对方法f()使用static关键字:

// AntherFile.cpp

#include

using namespace std;

static void f();

void f()

{

cout<<”f\n”;

}

现在,尽管编译每个源文件时都没有问题,但是连接不会成功,因为方法f()使用内部链接,这样源文件FirstFile.cpp中就不能使用这个方法了。定义了static方法,但是在源文件中没有使用,有些编译器会发出警告。

注意,此时在f()的定义前面不需要重复关键字static。

要达到上诉的内部(静态)连接的效果,还有一种方法就是:采用匿名命名空间。即把变量和函数包装在一个未命名的命名空间中,而不是使用static关键字。

//AntherFile.cpp

#include

using namespace std;

namespace {

void f();

void f()

{ cout<<”f\n”;}

}

声明了匿名命名空间中的实体之后,可以在同一源文件中的任意位置访问这些实体,但是在其他的源文件中不能访问。

c. 函数中的static变量

此种用法是创建局部变量,只在进入和退出变量作用域之间维护变量的值。函数内部的静态变量就像只能是只能从该函数访问的全局变量一样。静态变量的一种通常用法是“记住”是否都有已经为一个函数完成特定的初始化。

void performTask()

{

static bool inited = false;

if(!inited)

{ cout<<”initing\n”; inited = true;}

}

然而,static变量往往让人很糊涂,通常还有更好的方法来建立代码,而避免使用static变量。在这种情况下,可能想在编写类时,编写一些构造函数来完成所需的初始化工作。

要避免使用独立的static变量。应在对象内维护变量状态。

3. 关键字extern

它看起来与static相对立的,extern用来为声明外部链接。比如,对于const与typedef默认的都有内部链接,所以可以用extern为其指定外部链接。

把一个名字指定为extern时,编译器会把它当作声明而不会定义来对待。意味着编译器不会为其分配空间。必须为变量提供没有关键字extern的另外的定义。

//AntherFile.cpp

extern int x;

int x = 3; //等价于extern int x = 3;

上面的文件中可以不用extern,因为x默认的就有外部链接。

在下面文件中使用

//FirstFile.cpp

#include

using namespace std;

extern int x;

int main()

{ cout<<”x = ”<

如果此文件不用extern会导致连接失败,因为全局作用域内有二个x变量。

不过我们建议,尽可能不要使用全剧变量。全局变量容易让人迷惑,也很容易出错,尤其是在大型程序中。要完成这样一些功能,应该使用static类成员和方法。

4. 非局部变量的初始化顺序

程序中的全局变量和static类数据成员都是在main()函数开始运行前初始化的。给定源文件,会按照它们在该文件中出现的顺序初始化的。

然而,C++并没有指定也不能保证不同源文件中非局部变量的初始化顺序。

类型和类型强制转换:

typedef

为已有类型提供了一个新的名字。而并没有创建新类型-只是提供了引用原类型的新方法。

最常见的用法就是,当实际的类型名很麻烦的时候可以为其提供一个可管理的名字。

类型强制转换:

在C中使用()进行强制转换,C++中提供了四种新的类型强制转换方法:static_cast、dynamic_cast、const_cast、reinterpret_cast。应该多使用C++风格的类型强制转换。因为C++风格的类型强制转换会完成更多的类型检查。

const_cast:可以去除变量的常量性。这是这四种中唯一允许去除变量的常量性的类型强制转换。从理论上讲,应该不会需要进行const类型强制转换。如果变量