C++内存管理,const、mutable、static、编译过程

C++ 内存分区:

栈、堆、全局/静态存储区、常量存储区、代码区
C++内存管理,const、mutable、static、编译过程_第1张图片

  • 栈:存放函数的局部变量,函数参数,返回地址,由编译器自动分配和释放
  • 堆:动态申请的内存空间,一般由程序员手动申请和释放,如果没有手动释放,操作系统会自动回收。
  • 数据段/静态存储区:存放全局变量、静态变量,C语言中区分未初始化的放在.bss区,初始化过得放在.data区,C++中不在区分,在编译期为其分配内存,在程序结束时释放。(注意:若全局变量或静态变量未赋初值,系统会自动为其0初始化)
  • 代码段: 存放代码,不允许修改,但可以执行。编译后的二进制文件存放在这里。

变量区分

全局变量、局部变量、静态全局变量、静态局部变量的区别

  • 全局变量:全局作用域,只需要在一个源文件中声明,在其他源文件中都可以使用(使用extern)
  • 局部变量:局部作用域,只在函数执行期间存在,函数执行完成,释放。
  • 静态全局变量:文件作用域,仅作用于它定义的文件,其他没有定义的源文件不能使用。
  • 静态局部变量:局部作用域,只对定义自己的函数体始终可见,从定义到程序运行结束一直存在。

存储位置:
全局变量、静态全局变量、静态局部变量存放在数据段
局部变量存放在栈
注意正是因为全局变量的全局作用域的性质,我们在定义变量的时候如果频繁使用全局变量,当代码量多起来,就很容易造成命名污染。
总结:
静态变量用 static 告知编译器,自己仅仅在变量的作用范围内可见。

面试问题,static作用:

  1. 保持变量内容持久:静态修饰的变量放在数据段,生命周期加长。
  2. 隐藏:静态修饰之后,只有定义的源文件可以发现自己
  3. 作用于类的成员变量和类的成员函数,使得类变量或者类成员函数和类有关,不定义也可以使用
  4. 默认初始化为0,static修饰的变量和全局变量都放在静态存储区,在静态数据区,内存中所有的字节默认值都是0x00。

面试问题:const修饰的变量一定不可以修改吗?

不一定,首先const作用的时期是编译阶段,为修饰的变量加上只读特性,告诉编译器,这个变量我希望它不被改变。而他是不能对内存的存储位置进行改变的。
实际上,一个变量是否可以被改变取决于它在内存中的存储位置,如果变量存储在只读区域,它是不能被改变的,如果放在栈区、堆区,并不是只读的,那么它就是可以修改的。
比如,const修饰局部变量,const int a =10;我们不能通过变量名a=8对他进行修改,却可以通过指针的方式对其内存进行操作。实质原因是这块内存是放在栈区的。
而对于const修饰的全局变量,通过变量名不能修改,指针也不能操作,原因是,const修饰的全局变量存放在只读数据段,这块内存是不可写的。

要点:

  • const作用在编一阶段,修饰变量
  • define作用在预处理阶段,定义常量
  • const为其修饰的变量、指针、函数、参数附加只读特性,并没有说它变成了只读变量。实际的读写性质还是要看它的存储位置
  • const全局变量存储在只读数据段,编译期最初将其保存在符号表中,第一次使用时为其分配内存,在程序结束时释放

mutable

修饰变量,可以修改变量的读写性质,是为了突破const而设置的。加了mutable表示随时可变,不管是常方法还是常饮用,只要我mutable了,就是可变的。

使用场景:
VC版本的自动指针中,使用RAII思想体现智能,在析构方法中释放空间,导致的问题就是,如果使用string作为模板参数,由于是浅拷贝,拷贝构造时,就会将同一块空间释放两次,这是不合理的。
VC的解决办法就是,引入Owns拥有权,重写拷贝构造函数。发生拷贝构造时,只需要进行拥有权的转移,即可解决上述问题。但新的问题又来了,我们通常拷贝构造的参数都是const的引用,加了const的对象还怎么修改成员。

auto_ptr(const auto_ptr &Y)
{
	_Ptr = Y._Ptr;
	_Owns = Y._Owns;
	Y.Owns = false;      要这样做Y就不能加const
}

为了方便,有两种解决方法:

  1. 强制类型转换,将this指针不转换为非常的this
  2. 第二个就是为成员变量加上mutable关键字,突破const的限制。变成可以修改的变量。

VC 的 auto_ptr是这样做的。

_T* release()cosnt
{
	((auto_ptr<_T>*)this)-> _Owns = false;    //强制类型转换
	return *_Ptr;
}
auto_ptr(const auto_ptr& Y):_Owns(Y._Owns),
							_Ptr(Y.release())
{	}

如果我们为_Owns加上mutable也是可以的。

private:
	mutable bool _Owns;      //可变了
	_T* _Ptr;

编译过程

  • 预处理 :头文件展开/宏替换/去掉注释/条件编译
  • 编译:检查语法/生成汇编语言
  • 汇编:汇编代码转为机器代码
  • 连接:连接在一起生成可执行文件;链接静态库,会将函数代码部分拷贝到可执行文件,动态连接库会将函数的描述信息加载到可执行文件,比如重定向信息。

你可能感兴趣的:(c/c++,复习难点突破,c++,面试,开发语言)