变量存储说明符,限定符,类型转换


变量存储说明符与限定符

 [auto][static][register][extern][mutable][volafile][const作用][const的使用]

auto 动态存储(默认)

特点:

  • 自动变量的作用域仅限于定义该变量的个体内.在函数中定义的自动变量,只要在函数内有效,在复合语句中的自动变量,只在复合语句内有效
  • 自动变量属于动态存储方式,只有在使用它,即定义该变量的函数憋调用时,才给它分配存储单元,开始它的生命周期,函数调用结束,释放存储单元,结束生命周期,隐藏函数调用后自动变量的值不能保留.在符合语句中定义的自动变量,在退出符合语句后也不能在使用,否则引起错误

 例子:

auto int b; //等价于 int b 所有不加存储说明符的变量都默认为auto类型的变量

 

static 静态存储

作用:

  用静态存储方式存储变量,生存周期为进程的整个执行时间,

分类:

  • 在函数体外定义的叫静态全局变量(它可以用extern在其他文件中声明并引用此静态全局变量)
  • 在函数体内定义的静态变量叫做静态局部变量(它始终的存在,并且只能在声明的函数中可以访问其他的函数无权访问,系统在未赋初值情况下会自动赋0值)

静态全局变量的说明

  • 静态全局变量要是定义在.cpp文件中,那么这个变量只能被这个.cpp文件中的函数访问
  • 静态全局变量要是定义在.h文件中,那么这个变量可以被任何包含了这个.h文件的.cpp文件访问

例子:

 // 例1 -- 静态全局变量
 static int a;
 void func1()
 {
     a = 5;
 }
 void func2()
 {
     a = 7;
 }
 
 
 // 例2 -- 静态局部变量
 void func1()
 {
     static int a;
     a = 5;
 }
 void func2()
 {
     a = 7; //编译错误, 因为虽然变量 a 存在,但是 fun1 有它的访问权限,而 func2 没有
 }

下面是一个更复杂点的例子:

 // demo1.h -- 内容 *********************************
 #include "demo2.h"
 
 void demo1_func();
 void demo1_func1();
 void demo1_func2();
 
 
 // demo1.cpp -- 内容 *******************************
 #include "demo1.h"
 
 void demo1_func()
 {
     cout<<"the original value of a is "<<a<<endl;    
 }
 void demo1_func1()
 {
     a++;
     cout<<"demo1_func1 :    the value of a is "<<a<<endl;    
 }
 void demo1_func2()
 {
     a = 10;
     cout<<"demo1_func2 :    the value of a is "<<a<<endl;    
 }
 
 // demo2.h -- 内容 *********************************
 #include <iostream>
 using namespace std;
 
 static int a = 0;
 void demo2_func();
 void demo2_func1();
 void demo2_func2();
 
 // demo2.cpp -- 内容 *******************************
 #include "demo2.h"
 
 void demo2_func()
 {
     cout<<"the original value of a is "<<a<<endl;    
 }
 void demo2_func1()
 {
     a+=5;
     cout<<"demo2_func1 :    the value of a is "<<a<<endl;    
 }
 void demo2_func2()
 {
     a = 30;
     cout<<"demo2_func2 :    the value of a is "<<a<<endl;
 }
 
 // main.cpp -- 内容 *********************************
 #include "demo1.h"
 
 void main()
 {
     cout<<"调用 demo1 中的函数修改静态全局变量 a 的值:"<<endl;
     demo1_func();
     demo1_func1();
     demo1_func2();
     cout<<endl;
 
     cout<<"调用 demo2 中的函数修改静态全局变量 a 的值:"<<endl;
     demo2_func();
     demo2_func1();
     demo2_func2();
     cout<<endl;
 
     cout<<"在 main 函数修中改静态全局变量 a 的值:"<<endl;
     cout<<"the original value of a is "<<a<<endl;
     a += 3;
     cout<<"main( a += 3 ) :    the value of a is "<<a<<endl;
     a = 15;
     cout<<"main( a = 15 ) :    the value of a is "<<a<<endl;
     cout<<endl;
 
     system("pause");
 }

  让我猜猜运行结果,照理来说我们修改的都是同一文件中的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 这个变量

  为什么是每个访问这个static int a这个变量的.cpp文件,而不是为每个访问static int a这个变量的函数开辟一个备份呢?我们看看在主函数中调用func.cpp中的函数看看func.cpp中的static int a 这个变量值的那段输出结果,func(),func2()两个函数,函数中都有修改到static int a这个变量,虽然修改的结果是在下次调用函数时候才能体现,但是从结果中我们可以清楚的看到他们修改后的static int a都在另一函数中输出结果得到了体现!

 

register寄存器存储

作用:

  这种变量直接放在CPU的寄存器中,不需要访问内存,而直接从寄存器中读取,这样可提高效率

 

extern引用声明

说明:

按照默认规则,凡是在所有函数前,在函数外部定义的变量都是外部变量,定义时可以不写extern说明符,但是在一个函数体内说明一个以在函数体外(在函数体定义之前没有定义的变量)或别的程序模块中定义过的变量时,必须使用extern说明符,一个外部变量被定义后,它就分配了固定的内存空间.外部变量的生存周期为整个执行时间,即在程序的执行期间外部变量可以被随意使用,外部变量属于全局变量

作用:

  • 调用其他文件的函数,通过在正常的函数声明前加上extern声明外部函数
  • 调用其他文件的全局变量,通过在全局变量声明前加上extern声明外部函数

例子:

 // 形式一
  // ex.h -- 内容 *********************************
  #include <iostream>
  using namespace std;
  
  extern int a;
  extern void func();
  // ex.cpp -- 内容 *******************************
  #include "ex.h"
  
  int a = 0;
  void func()
  {
      a = 1;
      cout<<"the value of a is "<<a<<endl;    
  }
  // main.cpp -- 内容 *****************************
  #include "ex.h"
  
  void main()
  {
      func();
      a = 100;
      cout<<"the value of a is "<<a<<endl;
      system("pause");
  }
  
  
  
  // 形式二
  // ex.cpp -- 内容 *******************************
  int a = 0;
  void func()
  {
      a = 1;
      cout<<"the value of a is "<<a<<endl;    
  }
  // main.cpp -- 内容 *****************************
  #include <iostream>
  using namespace std;
  
  extern int a;
  extern void func();
  
  void main()
  {
      func();
      a = 100;
      cout<<"the value of a is "<<a<<endl;
      system("pause");
  }

输出结果

 

mutable

作用:

  mutable当结构体/类变量为const,其中用mutable声明的成员也可被修改

例子:

 class A
 {
 private:
     mutable int a;
     int b;
 public:
     A():a(0){}
     void play()const
     {
         a ++;
         b = 5; //错误,因为此时的变量 b 已经被视为常量
     }
 };

 [返回目录]

-------------------------------------------------------------------------------------------------------------

 

限定符

volafile

说明:    声明即使程序代码没有对内存单元进行修改,其值也可能发生变化

 

const

const 的作用

1)  便于进行类型检查,可以保护被修饰的东西,防止意外的修改,增强程序的健壮性

例子:

void f(const int i) { i=10;//error! }

2)   为函数重载提供了一个参考

例子:

class A 
{ 
private:
	int a;
public:
	void f(int i) 
	{
	  a = i;
	} //一个函数
	void f(int i) const 
	{
	  a = i;
	} //上一个函数的重载 
};

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 取消其定义从而限定其作用域

 

使用 const

1)  修饰一般常量,常量数组,常量对象(修饰符 const 可以用在类型说明符前,也可以用在类型说明符后,但是必须初始化)

例子:

// 例1 -- 定义常量
const int i=5; 
const int j;    //错误, 没有对常量进行初始化
// 例2 -- 定义常量数组
const int i[5] = {0,1,2,3,4};    //错误, 没有对常量数组进行初始化
// 例3 -- 定义常量对象 
class constClass
{
public:
	void show()
	{
		cout<<"this is  const class!"<<endl;
	}
};
void func()
{
	const constClass i;
}

2)  修饰指针

例子:

const int *A;            //const 修饰指向的对象,A可变,A指向的对象不可变
int const *A;           //const 修饰指向的对象,A可变,A指向的对象不可变
int *const A;           //const 修饰指针A, A不可变,A指向的对象可变
const int *const A;  //指针A和A指向的对象都不可变

3)  修饰引用

例子:

const double &v; //该引用所引用的对象不能被更新

4)  修饰类的成员函数:

作用:

任何不需修改数据成员的函数都应被指为const类,可减小函数对数据成员的修改,若修改了在编译时会报错. 这样,在调用成员函数时就不能修改类里面的数据

例子如上

5)  在另一连接文件中引用const常量

例子:

extern const int i; //正确的引用
extern const int j=10; //错误!常量不可以被再次赋值

看看extern的使用

6)  修饰函数参数,返回类型

作用:

  • 当修饰的是函数参数且传递是地址时,有保护实参的作用
  • const修饰符也修饰函数的返回值,是返回值不可被改变

例子如上

 [返回目录]



C++类型转换

 [隐式转换][static_cast][const_cast][reinterpret_cast][dynamic_cast][旧式 C 风格转换]

隐式转换

条件:

  • 在混合类型表达式中,其操作数被转换为同一类型(向精度高的转换)
  • 用作条件被转换为bool类型
  • 用一表达式初始化某个变量,或将一表达式赋值给某个变量,则该表达式被转换为该变量类型

 例子:

int a = 100;
double b =10.50;
b = a;   //隐式转换这里吧int类型的a转换成double类型然后赋值给b

-------------------------------------------------------------------------------------------------------------

强制转换

static_cast<type>(value)                         

作用: 

  • 用于类层次结构中基类和子类之间指针或引用的转换
      进行上行转换(把子类的指针或引用转换成基类表示)是安全的
      进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的
  • 用于基本数据类型之间的转换.如把int转换成char,把int转换成enum.这种转换的安全性也要开发人员来保证
  • 把空指针转换成目标类型的空指针
  • 把任何类型的表达式转换成void类型

解释:

  • l type:将要转换成类型
  • l value:变量名

例子:

double d=99.0;
char ch=static_cast<char>(d);

const_cast<type>(value)                          

作用:

  • 转换掉value的const的属性
  • 转换掉value的volafile的属性

用途:

  • 常量指针被转化成非常量指针,并且仍然指向原来的对象
  • 常量引用被转换成非常量引用,并且仍然指向原来的对象

解释:

  • l type:变量原来的类型
  • l value:变量名

例子:

// 例1 -- 常量指针
int a = 10;
const int *i = &a;
int* j = const_cast<int*>(i);

// 例2 -- 常量对象
int a = 10;
const int &i = a;
int j = const_cast<int&>(i);

// 例3 
//常量对象被转换成非常量对象时出错
const A ca;
A a = const_cast<A>(ca);  //不允许
//常量变量被转换成非常量变量时出错
const int i = 100;
int j = const_cast<int>(i);  //不允许

reinterpret_cast<type>(value)                   

作用:

  • 可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值).该运算符的用法比较多
  • 函数指针类型之间进行转换

要求

  • type-id必须是一个指针、引用、算术类型、函数指针或者成员指针
  • 不能将非32bit的数据转成指针

解释:

  • l type:   变量要转换成类型
  • l value:变量名

例子:

int *n = new int;
double *d = reinterpret_cast<double*>(n);
 /*仅重新解释了给出的对象的比特模型而没有进行二进制转换,所以仅仅是把n比特位复制给d,没有进行必要的分析,所以其要慎用*/

dynamic_cast< type * / & ><value>               

作用:

  • 用于类层次间进行转换
      上行转换(由子类指针转换成父类指针),dynamic_cast 和 static_cast 的效果是一样的
      下行转换(由父类指针转换成子类指针),dynamic_cast 具有类型检查的功能(通过虚函数表,所以子类必须有虚函数),比static_cast更安全
  • dynamic_cast 还支持交叉转换(cross cast),但是返回结果是 null

要求:

  type */&必须是类的指针或者引用

解释:

  • l type:要转换成类型
  • l value:变量名

例子:

 #include <iostream>
 using namespace std;
 
 class human
 {
 public:
     virtual void show()
     {
         cout<<"this is human!"<<endl;
     }
 };
 class man:public human
 {
 public:
     virtual void show()
     {
         cout<<"this is man!"<<endl;
     }
 };
 class woman:public human
 {
 public:
     virtual void show()
     {
         cout<<"this is woman!"<<endl;
     }
 };
 void result(human *ph,man* pm,woman* pw)
 {
     if(ph != NULL)
         ph->show();
     else
         cout<<"ph is NULL!"<<endl;
     if(pm != NULL)
         pm->show();
     else
         cout<<"pm is NULL!"<<endl;
     if(pw != NULL)
         pw->show();
     else
         cout<<"pw is NULL!"<<endl;
     cout<<endl;
 }
 void main()
 {
     human* ph = new human;
     man *pm = new man;
     woman *pw = new woman;
     cout<<"未转换前的结果"<<endl;
     result(ph,pm,pw);
 
     human *temph = ph;
     ph = dynamic_cast<human*>(pm);
     cout<<"ph与pm的经上行转换后的结果"<<endl;
     result(ph,pm,pw);
 
 
     
     ph = temph;
     man *tempm = pm;
     pm = dynamic_cast<man*>(ph);
     cout<<"pm与ph的经下行转换后的结果"<<endl;
     result(ph,pm,pw);
 
     pm = tempm;
     woman *tempw = pw;
     pw = dynamic_cast<woman*>(pm);    
     cout<<"pm与pw的经交叉转换后的结果"<<endl;
     result(ph,pm,pw);
 
     system("pause");
 }

输出结果

-------------------------------------------------------------------------------------------------------------

旧式c风格强制转换

格式:

  (类型名)(表达式)

例子:

int a = 100;
double b = 1.75 , c = 5.68;
a = (int)(b);        //此时a的值为1
a = (int)(b+c);     //此时a的值为7

 [返回目录]

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值