目录
变量存储说明符与限定符(const,static,extern,auto,mutable)
类型转换(const_cast,static_cast,dynamic_cast,reinterpret_cast)
(本章节中例子都是用 VS2005 编译调试的)
变量存储说明符与限定符
[auto][static][register][extern][mutable][volafile][const作用][const的使用]
特点:
- 自动变量的作用域仅限于定义该变量的个体内.在函数中定义的自动变量,只要在函数内有效,在复合语句中的自动变量,只在复合语句内有效
- 自动变量属于动态存储方式,只有在使用它,即定义该变量的函数憋调用时,才给它分配存储单元,开始它的生命周期,函数调用结束,释放存储单元,结束生命周期,隐藏函数调用后自动变量的值不能保留.在符合语句中定义的自动变量,在退出符合语句后也不能在使用,否则引起错误
例子:
1 auto int b; //等价于 int b 所有不加存储说明符的变量都默认为auto类型的变量
作用:
用静态存储方式存储变量,生存周期为进程的整个执行时间,
分类:
- 在函数体外定义的叫静态全局变量(它可以用extern在其他文件中声明并引用此静态全局变量)
- 在函数体内定义的静态变量叫做静态局部变量(它始终的存在,并且只能在声明的函数中可以访问其他的函数无权访问,系统在未赋初值情况下会自动赋0值)
静态全局变量的说明
- 静态全局变量要是定义在.cpp文件中,那么这个变量只能被这个.cpp文件中的函数访问
- 静态全局变量要是定义在.h文件中,那么这个变量可以被任何包含了这个.h文件的.cpp文件访问
例子:
1 // 例1 -- 静态全局变量
2 static int a;
3 void func1()
4 {
5 a = 5;
6 }
7 void func2()
8 {
9 a = 7;
10 }
11
12
13 // 例2 -- 静态局部变量
14 void func1()
15 {
16 static int a;
17 a = 5;
18 }
19 void func2()
20 {
21 a = 7; //编译错误, 因为虽然变量 a 存在,但是 fun1 有它的访问权限,而 func2 没有
22 }
下面是一个更抽象点的例子:
1 // demo1.h -- 内容 *********************************
2 #include "demo2.h"
3
4 void demo1_func();
5 void demo1_func1();
6 void demo1_func2();
7
8
9 // demo1.cpp -- 内容 *******************************
10 #include "demo1.h"
11
12 void demo1_func()
13 {
14 cout<<"the original value of a is "<<a<<endl;
15 }
16 void demo1_func1()
17 {
18 a++;
19 cout<<"demo1_func1 : the value of a is "<<a<<endl;
20 }
21 void demo1_func2()
22 {
23 a = 10;
24 cout<<"demo1_func2 : the value of a is "<<a<<endl;
25 }
26
27 // demo2.h -- 内容 *********************************
28 #include <iostream>
29 using namespace std;
30
31 static int a = 0;
32 void demo2_func();
33 void demo2_func1();
34 void demo2_func2();
35
36 // demo2.cpp -- 内容 *******************************
37 #include "demo2.h"
38
39 void demo2_func()
40 {
41 cout<<"the original value of a is "<<a<<endl;
42 }
43 void demo2_func1()
44 {
45 a+=5;
46 cout<<"demo2_func1 : the value of a is "<<a<<endl;
47 }
48 void demo2_func2()
49 {
50 a = 30;
51 cout<<"demo2_func2 : the value of a is "<<a<<endl;
52 }
53
54 // main.cpp -- 内容 *********************************
55 #include "demo1.h"
56
57 void main()
58 {
59 cout<<"调用 demo1 中的函数修改静态全局变量 a 的值:"<<endl;
60 demo1_func();
61 demo1_func1();
62 demo1_func2();
63 cout<<endl;
64
65 cout<<"调用 demo2 中的函数修改静态全局变量 a 的值:"<<endl;
66 demo2_func();
67 demo2_func1();
68 demo2_func2();
69 cout<<endl;
70
71 cout<<"在 main 函数修中改静态全局变量 a 的值:"<<endl;
72 cout<<"the original value of a is "<<a<<endl;
73 a += 3;
74 cout<<"main( a += 3 ) : the value of a is "<<a<<endl;
75 a = 15;
76 cout<<"main( a = 15 ) : the value of a is "<<a<<endl;
77 cout<<endl;
78
79 system("pause");
80 }
让我猜猜运行结果,照理来说我们修改的都是同一文件中的static int a这个变量,输出结果无非也就是a这变量被三个文件修改来修改去然后输出a的值,因为访问的都是同一变量么,可是运行后的结果却出乎人的意料,如下图所示:
为什么会这样,好像demo.cpp和demo2.cpp和main.cpp在访问static int a这个变量时候,static int a这个变量为每个访问它的.cpp文件都开了个备份,这样它们访问的就不是同一个static int a 这个变量,而是它们独自的static int a这个变量,所以它们的操作也不会影响到其他.cpp里面的static int a 这个变量.这个看起来好像有点奇怪,但是我们注意看下 a 变量定义的位置.static int a 这个变量定义在 demo2.h 这个文件中.然后在编译的时候 main.cpp 和 demo1.cpp 都会把 demo2.h 内容拷贝进来.也就是说相当于我们在 main.cpp 和 demo1.cpp 中都定义了一个 static int a 这个变量.那么结果应该可想而知了吧.再拿个例子来做说明吧:
#include<iostream>
using namespace std;
void func1()
{
static int a=5;
cout<<"func1:a="<<a++<<endl;
}
void func2()
{
static int a=5;
cout<<"func2:a="<<a++<<endl;
}
main()
{
func1();
func2();
func1();
func2();
}
/*********************************************
输出结果:
func1:a=5
func2:a=5
func1:a=6
func2:a=6
*********************************************/
所以注意不要在头文件中声明 static 变量和全局变量
作用:
这种变量直接放在CPU的寄存器中,不需要访问内存,而直接从寄存器中读取,这样可提高效率
说明:
按照默认规则,凡是在所有函数前,在函数外部定义的变量都是外部变量,定义时可以不写extern说明符,但是在一个函数体内说明一个以在函数体外(在函数体定义之前没有定义的变量)或别的程序模块中定义过的变量时,必须使用extern说明符,一个外部变量被定义后,它就分配了固定的内存空间.外部变量的生存周期为整个执行时间,即在程序的执行期间外部变量可以被随意使用,外部变量属于全局变量
作用:
- 调用其他文件的函数,通过在正常的函数声明前加上extern声明外部函数
- 调用其他文件的全局变量,通过在全局变量声明前加上extern声明外部函数
例子:
1 // 形式一
2 // ex.h -- 内容 *********************************
3 #include <iostream>
4 using namespace std;
5
6 extern int a;
7 extern void func();
8 // ex.cpp -- 内容 *******************************
9 #include "ex.h"
10
11 int a = 0;
12 void func()
13 {
14 a = 1;
15 cout<<"the value of a is "<<a<<endl;
16 }
17 // main.cpp -- 内容 *****************************
18 #include "ex.h"
19
20 void main()
21 {
22 func();
23 a = 100;
24 cout<<"the value of a is "<<a<<endl;
25 system("pause");
26 }
27
28
29
30 // 形式二
31 // ex.cpp -- 内容 *******************************
32 int a = 0;
33 void func()
34 {
35 a = 1;
36 cout<<"the value of a is "<<a<<endl;
37 }
38 // main.cpp -- 内容 *****************************
39 #include <iostream>
40 using namespace std;
41
42 extern int a;
43 extern void func();
44
45 void main()
46 {
47 func();
48 a = 100;
49 cout<<"the value of a is "<<a<<endl;
50 system("pause");
51 }
输出结果
作用:
mutable当结构体/类变量为const,其中用mutable声明的成员也可被修改
1 class A
2 {
3 private:
4 mutable int a;
5 int b;
6 public:
7 A():a(0){}
8 void play()const
9 {
10 a ++;
11 b = 5; //错误,因为此时的变量 b 已经被视为常量
12 }
13 };
-------------------------------------------------------------------------------------------------------------
限定符
volafile
说明: 声明即使程序代码没有对内存单元进行修改,其值也可能发生变化
const
1) 便于进行类型检查,可以保护被修饰的东西,防止意外的修改,增强程序的健壮性
1 void f(const int i) { i=10;//error! }
2) 为函数重载提供了一个参考
例子:
1 class A
2 {
3 private:
4 int a;
5 public:
6 void f(int i)
7 {
8 a = i;
9 } //一个函数
10 void f(int i) const
11 {
12 a = i;
13 } //上一个函数的重载
14 };
3) 提高了效率.编译器通常不为普通 const 常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高
4) 与宏 define 比较
- const 定义常量是有数据类型的,而#define宏定义常量却没有.
这样 const 定义的常量编译器可以对其进行数据静态类型安全检查,而 #define 宏定义的常量却只是进行简单的字符替换,没有类型安全检查,且有时还会产生边际效应(不如你愿处).所谓边际效应举例如下:
#define N 100
#define M 200 + N
当程序中使用 M*N 时,原本想要 100 * (200+ N )的却变成了 100 * 200 + N. - 有些调试程序可对 const 进行调试,但不对#define进行调试.
- 当定义局部变量时,const 作用域仅限于定义局部变量的函数体内.但用 #define 时其作用域不仅限于定义局部变量的函数体内,而是从定义点到整个程序的结束点.但也可以用 #undef 取消其定义从而限定其作用域
1) 修饰一般常量,常量数组,常量对象(修饰符 const 可以用在类型说明符前,也可以用在类型说明符后,但是必须初始化)
例子:
1 // 例1 -- 定义常量
2 const int i=5;
3 const int j; //错误, 没有对常量进行初始化
4 // 例2 -- 定义常量数组
5 const int i[5] = {0,1,2,3,4}; //错误, 没有对常量数组进行初始化
6 // 例3 -- 定义常量对象
7 class constClass
8 {
9 public:
10 void show()
11 {
12 cout<<"this is const class!"<<endl;
13 }
14 };
15 void func()
16 {
17 const constClass i;
18 }
2) 修饰指针
例子:
1 const int *A; //const 修饰指向的对象,A可变,A指向的对象不可变
2 int const *A; //const 修饰指向的对象,A可变,A指向的对象不可变
3 int *const A; //const 修饰指针A, A不可变,A指向的对象可变
4 const int *const A; //指针A和A指向的对象都不可变
3) 修饰引用
例子:
1 const double &v; //该引用所引用的对象不能被更新
4) 修饰类的成员函数:
作用:
任何不需修改数据成员的函数都应被指为const类,可减小函数对数据成员的修改,若修改了在编译时会报错. 这样,在调用成员函数时就不能修改类里面的数据
5) 在另一连接文件中引用const常量
例子:
1 extern const int i; //正确的引用
2 extern const int j=10; //错误!常量不可以被再次赋值
6) 修饰函数参数,返回类型
作用:
- 当修饰的是函数参数且传递是地址时,有保护实参的作用
- const修饰符也修饰函数的返回值,是返回值不可被改变
C++类型转换
[隐式转换][static_cast][const_cast][reinterpret_cast][dynamic_cast][旧式 C 风格转换]
条件:
- 在混合类型表达式中,其操作数被转换为同一类型(向精度高的转换)
- 用作条件被转换为bool类型
- 用一表达式初始化某个变量,或将一表达式赋值给某个变量,则该表达式被转换为该变量类型
例子:
1 int a = 100;
2 double b =10.50;
3 b = a; //隐式转换这里吧int类型的a转换成double类型然后赋值给b
-------------------------------------------------------------------------------------------------------------
作用:
- 用于类层次结构中基类和子类之间指针或引用的转换
进行上行转换(把子类的指针或引用转换成基类表示)是安全的
进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的 - 用于基本数据类型之间的转换.如把int转换成char,把int转换成enum.这种转换的安全性也要开发人员来保证
- 把空指针转换成目标类型的空指针
- 把任何类型的表达式转换成void类型
解释:
- l type:将要转换成类型
- l value:变量名
例子:
1 double d=99.0;
2 char ch=static_cast<char>(d);
作用:
- 转换掉value的const的属性
- 转换掉value的volafile的属性
用途:
- 常量指针被转化成非常量指针,并且仍然指向原来的对象
- 常量引用被转换成非常量引用,并且仍然指向原来的对象
解释:
- l type:变量原来的类型
- l value:变量名
例子:
1 // 例1 -- 常量指针
2 int a = 10;
3 const int *i = &a;
4 int* j = const_cast<int*>(i);
5
6 // 例2 -- 常量对象
7 int a = 10;
8 const int &i = a;
9 int j = const_cast<int&>(i);
10
11 // 例3
12 //常量对象被转换成非常量对象时出错
13 const A ca;
14 A a = const_cast<A>(ca); //不允许
15 //常量变量被转换成非常量变量时出错
16 const int i = 100;
17 int j = const_cast<int>(i); //不允许
作用:
- 可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值).该运算符的用法比较多
- 函数指针类型之间进行转换
要求
- type-id必须是一个指针、引用、算术类型、函数指针或者成员指针
- 不能将非32bit的数据转成指针
解释:
- l type: 变量要转换成类型
- l value:变量名
例子:
1 int *n = new int;
2 double *d = reinterpret_cast<double*>(n);
3 /*仅重新解释了给出的对象的比特模型而没有进行二进制转换,所以仅仅是把n比特位复制给d,没有进行必要的分析,所以其要慎用*/
dynamic_cast< type * / & ><value>
作用:
- 用于类层次间进行转换
上行转换(由子类指针转换成父类指针),dynamic_cast 和 static_cast 的效果是一样的
下行转换(由父类指针转换成子类指针),dynamic_cast 具有类型检查的功能(通过虚函数表,所以子类必须有虚函数),比static_cast更安全 - dynamic_cast 还支持交叉转换(cross cast),但是返回结果是 null
- dynamic_cast 基类转指正时候得到结果为 null
要求:
type */&必须是类的指针或者引用
解释:
- l type:要转换成类型
- l value:变量名
例子:
1 #include <iostream>
2 using namespace std;
3
4 class human
5 {
6 public:
7 virtual void show()
8 {
9 cout<<"this is human!"<<endl;
10 }
11 };
12 class man:public human
13 {
14 public:
15 virtual void show()
16 {
17 cout<<"this is man!"<<endl;
18 }
19 };
20 class woman:public human
21 {
22 public:
23 virtual void show()
24 {
25 cout<<"this is woman!"<<endl;
26 }
27 };
28 void result(human *ph,man* pm,woman* pw)
29 {
30 if(ph != NULL)
31 ph->show();
32 else
33 cout<<"ph is NULL!"<<endl;
34 if(pm != NULL)
35 pm->show();
36 else
37 cout<<"pm is NULL!"<<endl;
38 if(pw != NULL)
39 pw->show();
40 else
41 cout<<"pw is NULL!"<<endl;
42 cout<<endl;
43 }
44 void main()
45 {
46 human* ph = new human;
47 man *pm = new man;
48 woman *pw = new woman;
49 cout<<"未转换前的结果"<<endl;
50 result(ph,pm,pw);
51
52 human *temph = ph;
53 ph = dynamic_cast<human*>(pm);
54 cout<<"ph与pm的经上行转换后的结果"<<endl;
55 result(ph,pm,pw);
56
57
58
59 ph = temph;
60 man *tempm = pm;
61 pm = dynamic_cast<man*>(ph);
62 cout<<"pm与ph的经下行转换后的结果"<<endl;
63 result(ph,pm,pw);
64
65 pm = tempm;
66 woman *tempw = pw;
67 pw = dynamic_cast<woman*>(pm);
68 cout<<"pm与pw的经交叉转换后的结果"<<endl;
69 result(ph,pm,pw);
70
71 system("pause");
72 }
输出结果
-------------------------------------------------------------------------------------------------------------
格式:
(类型名)(表达式)
例子:
1 int a = 100;
2 double b = 1.75 , c = 5.68;
3 a = (int)(b); //此时a的值为1
4 a = (int)(b+c); //此时a的值为7