C++文件包含、引用、存储类
C++文件包含、引用、存储类
C++文件包含
“文件包含”是指一个源文件可以将另外一个源文件的全部内容包含进来,即将另外的文件包含到本文件之中。C++提供了#include命令用来实现“文件包含”的操作。如在file1.cpp中有以下#include命令:
#include ″file2.cpp″
include命令的两种形式
在#include命令中,文件名除了可以用尖括号括起来以外,还可以用双撇号括起来。#include命令的一般形式为:
#include <文件名>
或
#include ″文件名″
如:
#include <iostream>
或
#include ″iostream″
都是合法的。二者的区别是: 用尖括号时,系统到系统目录中寻找要包含的文件,如果找不到,编译系统就给出出错信息。
有时被包含的文件不一定在系统目录中,这时应该用双撇号形式,在双撇号中指出文件路径和文件名。
如果在双撇号中没有给出绝对路径,如#include ″file2.c″则默认指用户当前目录中的文件。系统先在用户当前目录中寻找要包含的文件,若找不到,再按标准方式查找。如果程序中要包含的是用户自己编写的文件,宜用双撇号形式。
对于系统提供的头文件,既可以用尖括号形式,也可以用双撇号形式,都能找到被包含的文件,但显然用尖括号形式更直截了当,效率更高。
新的C++标准库中的头文件一般不再包括后缀.h,例如:
#include <string>
但为了使大批已有的C程序能继续使用,许多C++编译系统保留了C的头文件,即提供两种不同的头文件,由程序设计者选用。如:
#include <iostream.h> //C形式的头文件
#include <iostream> //C++形式的头文件
效果基本上是一样的。建议尽量用符合C++标准的形式,即在包含C++头文件时一般不用后缀。如果用户自己编写头文件,可以用.h为后缀。
下面给出一个例子
建立两个文件:myinit.cpp文件和myA.cpp文件。
注意,自定义的头文件文件要么和源代码文件放在同一处,要么在编译的时候指明自定义头文件路径,这样编译器才能找到头文件,才能编译通过。
myinit.cpp文件内容如下:
int a = 5, b=3 ;
cout<<"a= "<<a<<" ,b= "<<++b<<endl ;
myA.cpp文件内容如下:
#include<iostream>
using namespace std;
int main()
{
int a = 36, b=98 ;
{
#include "myinit.cpp" //文件包含
a++ ;
}
b= b+16 ;
cout<<"a= "<<a<<" ,b= "<<++b<<endl ;
}
编译myA.cpp运行之,参见下图:
C++引用
引用是C++引入的新语言特性,是C++常用的一个重要内容之一。引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。
引用很容易与指针混淆,它们之间有三个主要的不同:
不存在空引用。引用必须连接到一块合法的内存。
一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
引用必须在创建时被初始化。指针可以在任何时间被初始化。
引用的声明方法:
类型标识符 &引用名=目标变量名;
int a;
int &ra=a; //定义引用ra,它是变量a的引用,即别名。在此声明中,&读作引用。
说明:
(1)&在此不是求地址运算,而是起标识作用。
(2)类型标识符是指目标变量的类型。
(3)声明引用时,必须同时对其进行初始化。
(4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。
ra=1;
等价于
a=1;
(5)声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址。&ra与&a相等。
(6)不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。
C++存储类
变量具有的属性有:数据类型(data type)、作用域((scope)、存储类(存储类别storage class)。
变量的作用域((scope)是从空间的角度来分析的,分为全局变量和局部变量。
变量的存储期(storage duration,也称生存期Life time) 指变量在内存中的存在期间。这是从变量值存在的时间角度来分析的。存储期可以分为静态存储期(static storage duration)和动态存储期(dynamic storage duration)。这是由变量的静态存储方式和动态存储方式决定的。
静态存储方式是指在程序运行期间,系统对变量分配固定的存储空间。动态存储方式则是在程序运行期间,系统对变量动态地分配存储空间。
内存中的供用户使用的存储空间的情况。这个存储空间可以分为三部分,即:
程序区
静态存储区
动态存储区
全局变量全部存放在静态存储区中,在程序开始执行时给全局变量分配存储单元,程序执行完毕就释放这些空间。在程序执行过程中它们占据固定的存储单元,而不是动态地进行分配和释放。
在动态存储区中存放以下数据:
函数形式参数。在调用函数时给形参分配存储空间。
函数中的自动变量(未加static声明的局部变量)。
函数调用时的现场保护和返回地址等。
这些数据,在函数调用开始时分配动态存储空间,函数结束时释放这些空间。在程序执行过程中,这种分配和释放是动态的,如果在一个程序中两次调用同一函数,则要进行两次分配和释放,而两次分配给此函数中局部变量的存储空间地址可能是不相同的。
存储类是一种类型说明符(type specifier),该说明符控制对象(如变量、函数)的作用域和存储期。
程序中大多数变量属于自动变量。若未指明存储类默认为自动(automatic)存储类。
函数中的局部变量,如果不用关键字static、extern等加以声明,编译系统对它们是动态地分配存储空间的。函数的形参和在函数中定义的变量(包括在复合语句中定义的变量)都属此类。在调用该函数时,系统给形参和函数中定义的变量分配存储空间,数据存储在动态存储区中。数据存储在动态存储区中。在函数调用结束时就自动释放这些空间。如果是在复合语句中定义的变量,则在变量定义时分配存储空间,在复合语句结束时自动释放空间。
用static声明静态局部变量
有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,即其占用的存储单元不释放,在下一次该函数调用时,该变量保留上一次函数调用结束时的值。这时就应该指定该局部变量为静态局部变量(static local variable)。换句话说,static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。
static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。有时希望某些全局变量只限于被本文件引用,而不能被其它文件引用,这时可以在定义全局变量时加上static。
在 C++ 中,当 static 用在类数据成员上时,会导致仅有一个该成员的副本被类的所有对象共享。
静态局部变量的例
#include <iostream>
using namespace std;
int f(int a) //定义f函数,a为形参
{
int b=0;
static int c=3; //定义c为静态局部变量
b=b+1;
c=c+1;
return a+b+c;
}
int main( )
{
int a=2,i;
for(i=0;i<3;i++)
cout<<f(a)<<" ";
cout<<endl;
return 0;
}
运行之,参见下图:
静态存储区内分配存储单元。在程序整个运行期间都不释放。虽然静态局部变量在函数调用结束后仍然存在,但其他函数是不能引用它的,也就是说,在其他函数中它是“不可见”的。
再给一个静态变量的例
#include <iostream>
using namespace std;
// 函数声明
void func(void);
static int count = 10; /* 全局变量 */
int main()
{
while(count--)
{
func();
}
return 0;
}
// 函数定义
void func( void )
{
static int i = 5; // 局部静态变量
i++;
std::cout << "变量 i 为 " << i ;
std::cout << " , 变量 count 为 " << count << std::endl;
}
运行之,参见下图:
用extern声明外部变量
全局变量(外部变量)是在函数的外部定义的,它的作用域为从变量的定义处开始,到本程序文件的末尾。在此作用域内,全局变量可以为本文件中各个函数所引用。编译时将全局变量分配在静态存储区。
有时需要用extern来声明全局变量,以扩展全局变量的作用域。
1) 在一个文件内声明全局变量
如果外部变量不在文件的开头定义,其有效的作用范围只限于定义处到文件终了。如果在定义点之前的函数想引用该全局变量,则应该在引用之前用关键字extern对该变量作外部变量声明,表示该变量是一个将在下面定义的全局变量。有了此声明,就可以从声明处起,合法地引用该全局变量,这种声明称为提前引用声明。
例、用extern对外部变量作提前引用声明,以扩展程序文件中的作用域。
#include <iostream>
using namespace std;
int max(int,int); //函数声明
int main()
{
extern int a,b;//对全局变量a,b作提前引用声明
cout<<max(a,b)<<endl;
}
int a=15,b=-7;//定义全局变量a,b
int max(int x,int y)
{
int z;
z=x>y?x:y;
return z;
}
运行之,参见下图:
在main后面定义了全局变量a,b,但由于全局变量定义的位置在函数main之后,因此如果没有程序的第5行,在main函数中是不能引用全局变量a和b的。现在我们在main函数第2行用extern对a和b作了提前引用声明,表示a和b是将在后面定义的变量。这样在main函数中就可以合法地使用全局变量a和b了。如果不作extern声明,编译时会出错,系统认为a和b未经定义。一般都把全局变量的定义放在引用它的所有函数之前,这样可以避免在函数中多加一个extern声明。
2) 在多文件的程序中声明外部变量
extern 可以用来在另一个文件中声明一个全局变量或函数。如果一个程序包含两个文件,在两个文件中都要用到同一个外部变量i,不能分别在两个文件中各自定义一个外部变量i。正确的做法是:在任一个文件中定义外部变量i,而在另一文件中用extern对i作外部变量声明,如:
extern int i;
例、本例用到两个文件:第一个文件main.cpp,第二个文件support.cpp。
main.cpp文件内容如下:
#include <iostream>
int count ;
extern void write_extern();
int main()
{
count = 5;
write_extern();
}
support.cpp内容如下:
#include <iostream>
extern int count;
void write_extern(void)
{
std::cout << "Count is " << count << std::endl;
}
现在,针对上面这个的多文件例子,演示如何使用Dev-C++进行多文件编写编译过程。
1)新建空白项目。按下图操作,添加一个空白项目:
2)为项目添加文件。按下图操作,为项目添加文件
3)编译运行。按下图操作,编译运行之
thread_local 存储类
使用 thread_local 说明符声明的变量仅可在它在其上创建的线程上访问。 变量在创建线程时创建,并在销毁线程时销毁。 每个线程都有其自己的变量副本。
thread_local 说明符可以与 static 或 extern 一起使用。
可以将 thread_local 仅应用于变量声明和定义,thread_local 不能用于函数声明或定义。
以下演示了可以被声明为 thread_local 的变量:
thread_local int x; // 命名空间下的全局变量
class X
{
static thread_local std::string s; // 类的static成员变量
};
static thread_local std::string X::s; // X::s 是需要定义的
void foo()
{
thread_local std::vector<int> v; // 本地变量
}
关于“C++存储类” 参考:
https://docs.microsoft.com/zh-cn/cpp/cpp/storage-classes-cpp?view=vs-2019