C++ 内存模型和名称空间

2014/11/10 01:07 am posted in  C++

和C一样,C++为内存存储数据方面提供了多种选择,可以选择数据保留在内存中的时间(生命域)以及程序的哪一部分可以访问数据(作用域和链接)等。

单独编译

和C语言一样,C++也允许甚至鼓励程序员将组件函数放在独立的文件中。可以单独编译这些文件然后将他们链接成可执行的程序。(IDE包含编译器和链接器的原由吧)。

头文件:包含结构声明和使用这些结构的函数的原型

源代码文件:包含与结构有关的函数的代码

源代码文件:包含调用与结构相关的函数代码

不要将函数定义或变量声明放在头文件中,对于简单的情况是可行的,但是如果存在重复包含头文件的文件,则会出现一个程序中将包含同一个函数的两个定义,除非函数式内联的,否则这将出错。

头文件经常包含的内容:

  • 函数原型
  • 使用define或const定义的符号常量
  • 结构声明
  • 类声明
  • 模板声明
  • 内联函数

将结构声明放在头文件中是可以的,因为它们不创建变量,而只是在源代码文件中声明结构变量时,告诉编译器如何创建该结构变量。同样,模板声明不是将被编译的代码,它们指示编译器如何生成与源代码中函数调用相匹配的函数定义。被声明为const的数据和内联函数有特殊的链接属性,因此可以放在头文件。

 存储持续性、作用域和链接性

在C++11中,有四种不同的方案来存储数据,这些方案的区别在于数据保留在内存中的时间。

  • 自动存储持续性(自动变量)
  • 静态存储持续性(静态变量,全局变量)
  • 线程存储持续性(C++11,使用关键字thread_local)
  • 动态存储持续性(动态内存)

作用域描述了名称在文件的多大范围内可见。

作用域为局部的变量只在定义它的代码块中可用。代码块是由花括号括起来的一系列语句。作用域为全局(也叫做文件作用域)的变量在定义位置到文件结尾之间都可用。自动变量的作用域为局部,静态变量的作用域是全局还是局部取决于是如何定义的:

链结性描述了名称如何在不同单元间共享:外部链接在文件间共享,内部链接只能有一个文件中的函数共享,自动变量没有链结性,因为它们不能被共享。

mutable说明符指出,即使结构变量为const,其某个成员也可以被修改

struct data
{
char name[30];
mutable int accesses;
...
};

又是一个神器。。

new支持定位操作,

char s[100];
int *p = new(s) int[10];

便可以在s的空间中申明出一个10个元素的int数组。神奇。

但是,因为毕竟不是在堆中开的内存,用delete会错误!

名称空间

在C++中,名称可以是变量、函数、结构、枚举、类以及类和结构的成员。当随着项目的增大,名称相互冲突的可能性增大。使用多个厂商的类库时,可能导致名称冲突。因此有了名称空间。

传统的C++名称空间,

声明区域是可以在其中声明的区域比如函数外的全局变量。

潜在作用域,变量在潜在作用域从声明点开始,到其声明区域结尾,因此潜在作用域比声明区域小,这是由于变量必须定以后才能使用。

一个名称空间不会和另外一个名称空间冲突。

名称空间可以是全局也可以位于另外一个名称空间中,但不能位于代码块中,因此,默认情况下在名称空间中声明的名称的链接性为外部。

名称空间定义起来很简单:

namespace Jill
{
char fetch(){...}
...
}

使用起来也简单,

Jill::fetch();

为了编程简单,于是有了using声明和using编译指令,

using Jill::fetch;

之后这个函数就可以正常使用了,

using namespace Jill;

之后,这个名称空间里的东西就可以正常使用了。

如果就着尽量节省的原则,前者可以节省更多的代码长度,因为没有用到的函数等其他名称空间的东西没被声明定义。

就着安全的原则,推荐使用第一种以避免多名称空间中的名称冲突。

名称空间是可以嵌套的,不过也很简单。

名称空间可以未命名,因为名称空间定义之后,里面的东西就相当于全局变量,但是未命名就限制了此空间内的东西只能在当前文件使用。

名称空间及其用途

  • 使用已命名的名称空间中声明的变量,而不是使用外部全局变量
  • 使用已命名的名称空间中声明的变量,而不是使用静态全局变量
  • 仅将using作为一种将旧代码转换为使用名称空间的权宜之计
  • 不要在头文件中使用using编译指令,非要添加using的话,只少要保证include的正确顺序
  • 导入名称空间时首选使用作用解析运算符或是using声明而不是using编译指令
  • 对于using声明,,首选将其作用域设置为局部而不是全局

总之,名称空间的引入是为了管理项目。