#ifndef COORDIN_H_//用于对该名称的定义
#define COORDIN_H_
...
#endif
马上就要回学校了,难免思绪就又会回到考研上面,不过最近在纠结与自己要考计算机的统考,还是一些院校的自主招生呢?作为一名跨专业的学生,甚是纠结。我看网上评论都是讲统考要公平些,但是自主招生更容易些,纠结ing。。。
从这章开始再往后,我应该会写的比较有整体性了,毕竟书本内容越来越接近于类这个概念了。本章就主要讲了内存模型和名字空间,估计要用两篇博文才可以搞定。
6.1 单独编译
和C语言一样,C++允许甚至是鼓励程序员将组件函数挡在单独的文件中。之前介绍过,可以单独编译这些文件,然后将它们链接成可执行的程序。如果只修改了一个文件,则可以只重新编译,然后将它与其他文件的编译版本链接。这是得对程序的管理更加方便。
C和C++程序员都不希望出现更多的问题,因此他们提供了#define来处理。语气将结构声明放在每一个文件中,不如将其放在头文件中,然后让每一个源代码文件都包含该文件。这样就把程序分成了三部分。
- 头文件:包含结构声明和使用结构的函数的原型。
- 源代码文件:包含于函数有关的函数的代码。
- 源代码文件:包含地哦啊用与结构相关的函数的代码。
请不要将函数定义或者是变量的声明放在头文件中,有事会引来不必要的麻烦。比如,如果在头文件中包含函数定义,然后在其他的文件中也包含该头文件,则将会引起函数的重复定义,这将会引起出错。下面列出了头文件常常包含的内容。
- 函数原型。
- 使用#define或const定义的符号常量。
- 结构声明。
- 类声明。
- 模板声明。
- 内联函数。
另外应注意的是,在包含头文件时,我们使用“coordin.h“,而不是<coordin.h>。如果文件名包含在尖括号中,则c++编译器将在存储标准头文件的主机系统的文件系统中查找;如果文件包含在双引号中,则编译器将首先查找当前的工作目录或者是源代码目录。如果都没找到才会在标准位置查找。
6.2 头文件的管理
在同一个文件中只能将同一个头文件包含一次。记住这个规则很容易,但很可能在不知情的情况下包含多次。有一种标准的C++技术可以避免多次包含同一个头文件。它是基于预处理器的编译指令#ifndef的。下面的代码意味着仅当以前没有使用预处理器编译指令#define定义的COOR_DIN_H_时,才处理#ifndef和#endif之间的语句:
#ifndef COORDIN_H_//用于对该名称的定义
#define COORDIN_H_
...
#endif
文件首次遇到该文件时,名称COORDIN_H_没有定义(我们根据include文件名来选择名称,并加上一些下划线,创建一个在其他地方不太可能定义的名称。若在同一个文件中遇到其他包含coordin.h的代码,编译器将知道COORDIN_H_被定义,从而跳到#endif后面的一行上。注意这种方法并不能防止编译器将文件包含两次,而只是让它忽略了第一次包含之外的所有内容。
6.3 存储持续性、作用域和链接性
C++使用三种不同的的方案俩存储数据,这些方案的区别在于数据保留在内存中的时间。
- 自动存储持续性:在函数定义的中声明的变量的存储持续性为自动的。它们在程序开始执行其所属的函数或代码块是被创建,在执行完函数或代码块时,内存被释放。
- 静态存储持续性:在函数定义外定义的变量和使用关键字static第一的变量的存储持续性都为静态。它们在程序的运行期间都存在。
- 线程存储持续性:如果变量使用thread_local声明的,则其生命周期与所属的线程一样长。
- 动态存储持续性:用new运算符分配的内存一直存在,直到使用delete运算符将其释放或程序的结束。这种内存的存储时序性为动态的,yoshi被称为自由存储或堆。
作用域描述了名称在文件的多大范围内可见。链接性描述了名称如何在不同的单元间共享。链接性为外部的名称可在文件间共享,链接性为内部的名称只能由一个文件中的函数共享。自动变量没有链接性。
6.4 自动存储持续性
如果在代码块中定义了变量,则该变量的存在时间和作用域将被限制在该代码块中。下面看下例子
int main()
{
int adt=5;
{
cout<<"Hello\n;
int bdt=-2;
cout<<bdt<<' '<<adt<<endl;
}
...
}
adt变量在内部代码块和外部代码块中都是可见的,而bdt仅在内部代码块中可见。
由于自动变量的数目随着函数的开始和结束而增减,因此程序必须在运行时对自动变量进行管理。常用的方法是流出一段内存,并将其视为栈。当函数被调用时,其自动变量将被加到栈中,栈顶指针指向变量后面的下一个可用的内存单元。函数结束时,栈顶指针被重置为函数被调用前的值,从而释放新变量使用的内存。下面来看一个例子加深一下理解。
(1)调用前的栈
栈顶 (下一次添加在这里) |
1890 (以前压入战中的值) |
2929 |
(2)函数调用后的栈
将函数的参数压入栈中fib(18,50L);
栈顶 |
50L long值站4字节 |
18 int值站两字节 |
1890 |
2929 |
(3)函数开始执行后的栈
void fib(int real,long tell)
{ ... }
栈顶 |
50L 函数执行与栈关联 |
18 函数执行与栈关联 |
1890 |
2929 |
(4)函数结束后的栈
50L |
18 栈顶回到原来位置 |
1890 |
2929 |
6.5 静态持续变量
和C语言一样,C++也为静态存储持续性变量提供了3中链接性:外部链接性(可在其他文件中访问)、内部链接性(只能在当前文件中访问)、和无链接性(只能在当前函数或者是代码块中访问)。由于静态变量的数目在程序的运行期间是不变的,因此程序不需要使用特殊的装置来管理他们。编译器将分配固定的内存块来存储所有的静态变量,这些变量在整个程序的运行期间都是存在的。另外,若果没有显示的初始化静态变量,编译器将把他设置为0.下面主要介绍三种静态变量:
int global=1000;//static duration,external linkage
static int one_file=50;//static,internal linkage
int main()
{
...
}
void func1(int n)
{
static int count=0;//static duration,no linkage
int llama=0;
...
}
所有的静态变量在整个的程序执行期间都是存在的。cout作用域为局部且没有链接性,其就像自动变量llama一样。而global和one_file则作用于整个文件,但区别在于global的链接性为外部的,可以在出本程序外的其他文件中使用它。
6.6静态持续性、外部链接性
链接性为外部的变量通常称之为外部变量,它们的存储连续性为静态的,作用于整个文件。下面我们先来看看外部变量的定义。一方面,在每个使用外部变量的文件中,都必须声明它;另一方面,C++有单定义原则,该规则指出,变量只能定义一次。为了满足这种需求,C++提供了两种变量的声明。一种是定义声明或简称定义,它给变量分配存储空间;另一种是引用声明或简称声明。
引用声明使用关键字extern,且不进行初始化;否则声明为定义,导致分配存储空间。如果要在多个文件中使用外部变量,只需在一个文件中包含该文件的定义,但在使用该变量的其他文件中,都必须使用关键字extern声明他:
flie1.cpp
#include"mystuff.h"
//defining an external variableint
var=0;
void promise();
int main()
{ ...}
这个文件定义了变量var,使得编译器为他分配空间。这是我们要在另一个文件中使用它,则要使用extern关键字
file2.cpp
#include"mystuff.h"
extern var;
int main()//这里需要注意的是我的文件2并不需要包含文件1就可直接利用extern来访问外部变量
{
...
}
6.6静态持续性、内部链接性
将static限定符用于作用域为整个变量时,该变量的链接性将为内部的。如果要在其他文件中使用相同的名称来表示其他变量,该如何办呢?比如
//file 1
int errors=20;
int main()
{ ... }
-----------------------------------
//file 2
int errors=5;
int main()
{ ... }
---------------------------------
//file 3
static int errors=5
int main()
{ ... }
文件1的作法实不可取的,因为它违反了但定义的原则,但是文件3却是可取的。这并没有违反单定义原则,因为关键字static指出标示符errors的链接性为内部的。因此并没有提供外部编译。
6.7 说明符和限定符
有些被称为存储说明符或c-v限定符的C++关键字提供了其他有关存储的信息。下面是存储说明符
- auto;
- register;
- extern;
- thread_local;
- mutable;
下面来简要的介绍一下上面的概念:
(1)cv限定符
- const;
- volatile;
cv表示const和volatile。最常用的的为const,在之前的介绍中一大概讲解了它。下面主要介绍volatile。该关键字的作用是为了改善编译器的优化能力。比如,假设编译器发现,程序在几条语句中两次使用了某个变量,则编译器可能不是让程序查找这个值两次,而是将这个值缓存到寄存器中。如果不讲变量声明为volatile,则编译器将进行这种优化;否则将不要进行这种优化。
(2)mutable
可以用它来指出,即使结构或类变量为const,它的某个成员也是可以被修改的
struct data
{
char name[30];
mutable int access;
...
}
此时我有如下代码,则是允许的
const data veep={"aaaa",0,...};
strcpy(veep.name,"cccc"};//not allowed
veep.access++;//allowed