1.单独编译
程序可以分成头文件(包含结构声明和使用这些结构的函数的原型),源代码文件(包含与结构有关的函数的代码),源代码文件(包含调用与结构相关的函数代码)。头文件中常包含函数原型,使用#define或const定义的符号常量,结构声明,类声明,模板声明,内联函数。在使用自己定义的头文件时使用#include“ haha.h”,如果为<>时,c++将在存储标准头文件的主机系统的文件系统中查找,在双引号中首先查找当前的工作目录或源代码目录。
避免多次包含同一个头文件:基于预处理器编译指令#infdef(if not defined),仅当以前没有使用预处理器编译指令#define定义名称COORDIN_H的时候,才处理#infdef和#endif之间的语句。
#ifndef COORDIN_H
#define COORDIN_H
…
#endif
当编译器首次遇到该文件时,名称COOEDIN_H未定义,编译器才查看#ifndef和#endif之间的内容,并读取定义COORDIN_H的一行,如果在同一个文件中遇到其他包含coordin.h的代码,将跳到endif后面的一行上。不过该方案只能让它忽略除第一次包含之外的所有内容。
2.存储持续性、作用域和链接性
C++使用三种不同方案存储数据,这些方案的区别在于数据保留在内存中的时间。
自定存储持续性:在函数定义中声明的变量(包括函数参数)存储持续性是自动的。
静态存储持续性:在函数外定义的变量和使用关键字static定义的变量的存持续性都为静态,在程序整个运行过程中都存在。
线程存储持续性:多核处理器的cpu可以同时处理多个执行任务。这让程序能够将计算放在可并行处理的不同线程之中。如果变量使用关键字thread_local声明的,那么其生命周期与所属线程一样长。
动态存储持续性:用new运算符分配的内存将一直存在,直到delete运算符将其释放或程序结束为止。这种内存的存储持续性为动态,也叫自由存储或者堆。
链接性为外部的名称可在文件间共享,链接性为内部的名称只能由一个文件内的函数共享。自动变量的名称没有链接性,因此它们不能共享。
自动变量和栈:因为自动变量的数目随函数的开始和结束增减,为了实现对自动变量的管理,常常留出一段内存视为栈。新数据被象征性地放在原有数据的上面(在相邻内存单元中而不是同一个内存单元中),当程序使用完后将其从栈删除。程序使用两个指针来追踪栈,一个指向栈底(栈的开始位置),一个指向栈顶(下一个可用内存单元)。当函数被调用时,其自动变量将被加入栈中,栈顶指针指向变量后面的下一个可用的内存单元。函数结束时栈顶指针被重置为函数被调用前的值,从而释放新变量使用的内存。
栈是LIFO后进先出的,调用函数时将实参压入栈底(起始位置)中,栈顶指针指向下一个内存单元。函数开始执行后,将栈中的实参值和形参关联起来,函数结束后栈顶恢复到原来的位置。
C++为静态存储持续变量提供了三种链接性:外部链接性(可在其他文件中访问),内部链接性(只能在当前文件中访问)和无链接性(只能在当前函数或者代码块中访问)。这三种链接性在整个程序执行期间都是存在的,由于静态变量的数目在程序运行期间是不变的,编译器将分配固定的内存块来存储所有的静态变量。
int global=1000;//外部链接性,别的文件可以使用
static int one_file=50;//内部链接性,文件内访问
int main(){…}
void funct1(int n)
{
Static int count=0;//无链接性(当前函数或者代码块中访问),与自动变量llama的不同是,即使funct1不执行,count也留在内存中。
Int llama=0;
}
Void funct2(int q){…}
即创建链接性为外部的静态持续变量必须在代码块的外面声明它,创建链接性为内部的静态持续变量,必须在代码块的外面声明它并且使用const限定符,创建没有链接性的静态持续变量必须在代码块内声明它。并使用static限定符。注意,未被初始化的静态变量的所有位都被设置成0,这种变量被称为零初始化。
静态变量的初始化:零初始化是必须的并且与常量表达式初始化统称为静态初始化,也可以进行动态初始化,即在编译后进行初始化,如右值调用了某个函数,注意调用sizeof运算符使用常量表达式初始化。
静态持续性、外部链接性:
外部变量(链接性是外部的变量,也叫全局变量),它们的存储持续性为静态,作用于是整个文件。注意定义给变量分配空间,声明不给变量分配存储空间,因为它引用已有的变量。如double up;是定义并且已经赋值0,使用extern int blem;是声明;extern char gz=‘z’;是定义,虽然用了extern但是初始化了。即使用extern声明变量且不进行初始化时才是声明。
单定义规则(变量只能有一次定义,与赋值区分开):如果要在多个文件中使用外部变量,只需要在一个文件中包含其定义,但是在使用其他变量的所有文件中,使用extern进行声明。::warming;使用域作用符表示使用全局变量。
静态持续性,内部链接性:
如果一个文件中定义了外部变量,另一个文件意图定义一个相同名称的内部变量,那么应该使用static 关键字来说明是内部链接性。
无链接性的局部变量:,将static限定符用于在代码块中定义的变量,即在改代码块不活动的时候也存在。因此在两次函数调用之间,静态局部变量的值将保持不变。程序只初始化一次静态局部变量。
3.说明符和限定符
volatile关键字表明,假设编译器发现程序在几条语句中两次使用了某个变量,则编译器可能不是查找这个值两次而是将这个值缓存到寄存器里。Volatile关键字相当于不要进行这种优化。
Mutable:将其使用在结构或者类内的变量可以实现,即使结构或者类变量为const,其某个成员也可以被修改。
Const:注意在c++看来,默认情况下全局变量的链接性为外部的,但const全局变量的链接性为内部的。即全局const定义就像使用了static说明符一样。但是可以使用extern关键字覆盖默认的内部链接性。extern const int states=50;必须在所有使用该常量的文件中使用extern关键字来声明它。另外注意,单个const在多个文件中共享,只有一个文件可以对其进行初始化。
函数和链接性:
所有函数的存储持续性都自动为静态的,在整个程序执行期间存在。默认函数具有外部链接性可在文件之间共享。可在函数原型中使用extern指出函数是在另一个文件中定义的。也可以使用static将函数设置为内部使之只能在一个文件中使用。必须同时在函数原型和函数定义中都使用static。单定义规则也适用于非内联函数。C++执行名称修饰,名称名称矫正对重载函数生成不同的符号名称(因为c++允许重名函数)。
extern “C” void spiff(int);//使用C链接性
extern “C++” void spiff(int);//显示使用c++链接
extern void spaff(int);//隐式使用c++链接
动态分配:
动态内存由运算符new和delete控制,而不是由作用域和链接性规则控制。因此允许在一个函数中分配动态内存,在另一个函数中释放。通常编译器使用三块独立的内存:一块用于静态变量,一块用于自动变量,一块用于动态存储。虽然存储方案概念不适用于动态内存,但适用于用来跟踪动态内存的自动和静态指针变量。如:
float *p_fees=new float[20];由new分配的内存一直保留在内存中,直到使用delete释放。但当包含该语句的代码块执行完时,p_fees指针将消失。如果另一个函数还要使用这80个字节的内容,那么必须将其地址传递或者返回给函数。若将p_fees声明为外部的则文件中位于该声明后后面的所有函数都可以使用它。在另一个文件中extern float *p_fees;声明则可以使用该指针。
New运算符初始化:
基本类型: int *pi=new int (6); double * pd=new double (99.99);
初始化常规结构或者数组需使用大括号的列表初始化(支持c++11):
Struct where {double x; double y; double z};
Where *one =new where {2.5,5.3,7.2};
Int *ar=new int[4] {2,4,6,7};
New失败时,引发std::bad_alloc异常。
New:运算符、函数和替换函数
Void *operator new(std::size_t);
Void *operator new[](std::size_t);
这些被称为分配函数位于全局名称空间中。也有由delete和delete[]调用的释放函数。
Void operator delete(void *);
Void operator delete[](void *);
定位new运算符:new还有一种变体被称为定位new运算符,用以指定要使用的位置。需要#include
假设p1是double 指针,如果使用定位new运算符时候,而buffer是char数组,那么需要
(void *)buffer强制转换。注意定位new运算符使用传递给它的地址,它不跟踪哪些内存单元已经被使用,也不查找未使用的内存块,所以如果不想覆盖旧数据,在下次写入的时候应该使用偏移量来申请新的内存:pd2=new (buffer +N*sizeof(double)) double[N];
Delete运算符只能用于这样的指针:指向常规new运算符分配的堆内存。即此时定位new分配在静态内存中,所以不能用delete删除。
4.名称空间
声明区域:可在其中进行声明的区域。
潜在作用域:变脸从声明点开始到其声明区域的结尾。
作用域:变量对程序而言可见的范围被称为作用域。
新的名称空间:通过定义一种新的声明区域来创建命名的名称空间。使用关键字namespace
Namespace jack
{
Double pail;
Void fetch();
Struct hill();
}
名称空间可以是全局的(外部的),也可以位于另一个名称空间中,但不能位于代码块中。可以在该文件或者另外的文件中使用jack的名称空间为fetch的代码。使用域作用夫::,如
Jack::pail叫限定的名称。
注意:假设名称空间和声明区域定义了相同的名称,如果试图使用using声明将名称空间的名称导入该声明区域,则这两个名称将发生冲突而导致错误。如果使用using编译指令将该名称空间的名称导入该声明区域,则局部版本将隐藏名称空间的版本。
Char fetch;
Int main()
{
Using namespace jack;
Double fetch; //此时fetch就是局部版本的fetch。
}
Int main()
{
Using jack::fetch;
Double fetch;//错误,此时已经有fetch的定义了。
}
名称空间其他特性:
Namespace elements
{
Namespace fire
{
Int flame;
}
Float water;
}
此时flame是using namespace elements::flame;也可以在名称空间中声明和编译其他名称空间的名称。Using编译指令是可以传递的,A>B,B>C,那么A>C;即会导入一个名称空间以及其包含的名称空间。也可以给名称空间创建别名,namewpace mvft=my_very_favorate_things; 未命名的名称空间相当于static变量,相当于链接性为内部的静态变量。
在做多文件工程项目时,头文件中声明名称空间,常量、结构定义和函数原型都可以加入其中。一个源文件用来完成位于名称空间中的函数定义,且必须在其对应的名称空间中定义。还需要一个源文件代码,使用这些函数、结构和函数原型。
1.单独编译
程序可以分成头文件(包含结构声明和使用这些结构的函数的原型),源代码文件(包含与结构有关的函数的代码),源代码文件(包含调用与结构相关的函数代码)。头文件中常包含函数原型,使用#define或const定义的符号常量,结构声明,类声明,模板声明,内联函数。在使用自己定义的头文件时使用#include“ haha.h”,如果为<>时,c++将在存储标准头文件的主机系统的文件系统中查找,在双引号中首先查找当前的工作目录或源代码目录。
避免多次包含同一个头文件:基于预处理器编译指令#infdef(if not defined),仅当以前没有使用预处理器编译指令#define定义名称COORDIN_H的时候,才处理#infdef和#endif之间的语句。
#ifndef COORDIN_H
#define COORDIN_H
…
#endif
当编译器首次遇到该文件时,名称COOEDIN_H未定义,编译器才查看#ifndef和#endif之间的内容,并读取定义COORDIN_H的一行,如果在同一个文件中遇到其他包含coordin.h的代码,将跳到endif后面的一行上。不过该方案只能让它忽略除第一次包含之外的所有内容。
2.存储持续性、作用域和链接性
C++使用三种不同方案存储数据,这些方案的区别在于数据保留在内存中的时间。
自定存储持续性:在函数定义中声明的变量(包括函数参数)存储持续性是自动的。
静态存储持续性:在函数外定义的变量和使用关键字static定义的变量的存持续性都为静态,在程序整个运行过程中都存在。
线程存储持续性:多核处理器的cpu可以同时处理多个执行任务。这让程序能够将计算放在可并行处理的不同线程之中。如果变量使用关键字thread_local声明的,那么其生命周期与所属线程一样长。
动态存储持续性:用new运算符分配的内存将一直存在,直到delete运算符将其释放或程序结束为止。这种内存的存储持续性为动态,也叫自由存储或者堆。
链接性为外部的名称可在文件间共享,链接性为内部的名称只能由一个文件内的函数共享。自动变量的名称没有链接性,因此它们不能共享。
自动变量和栈:因为自动变量的数目随函数的开始和结束增减,为了实现对自动变量的管理,常常留出一段内存视为栈。新数据被象征性地放在原有数据的上面(在相邻内存单元中而不是同一个内存单元中),当程序使用完后将其从栈删除。程序使用两个指针来追踪栈,一个指向栈底(栈的开始位置),一个指向栈顶(下一个可用内存单元)。当函数被调用时,其自动变量将被加入栈中,栈顶指针指向变量后面的下一个可用的内存单元。函数结束时栈顶指针被重置为函数被调用前的值,从而释放新变量使用的内存。
栈是LIFO后进先出的,调用函数时将实参压入栈底(起始位置)中,栈顶指针指向下一个内存单元。函数开始执行后,将栈中的实参值和形参关联起来,函数结束后栈顶恢复到原来的位置。
C++为静态存储持续变量提供了三种链接性:外部链接性(可在其他文件中访问),内部链接性(只能在当前文件中访问)和无链接性(只能在当前函数或者代码块中访问)。这三种链接性在整个程序执行期间都是存在的,由于静态变量的数目在程序运行期间是不变的,编译器将分配固定的内存块来存储所有的静态变量。
int global=1000;//外部链接性,别的文件可以使用
static int one_file=50;//内部链接性,文件内访问
int main(){…}
void funct1(int n)
{
Static int count=0;//无链接性(当前函数或者代码块中访问),与自动变量llama的不同是,即使funct1不执行,count也留在内存中。
Int llama=0;
}
Void funct2(int q){…}
即创建链接性为外部的静态持续变量必须在代码块的外面声明它,创建链接性为内部的静态持续变量,必须在代码块的外面声明它并且使用const限定符,创建没有链接性的静态持续变量必须在代码块内声明它。并使用static限定符。注意,未被初始化的静态变量的所有位都被设置成0,这种变量被称为零初始化。
静态变量的初始化:零初始化是必须的并且与常量表达式初始化统称为静态初始化,也可以进行动态初始化,即在编译后进行初始化,如右值调用了某个函数,注意调用sizeof运算符使用常量表达式初始化。
静态持续性、外部链接性:
外部变量(链接性是外部的变量,也叫全局变量),它们的存储持续性为静态,作用于是整个文件。注意定义给变量分配空间,声明不给变量分配存储空间,因为它引用已有的变量。如double up;是定义并且已经赋值0,使用extern int blem;是声明;extern char gz=‘z’;是定义,虽然用了extern但是初始化了。即使用extern声明变量且不进行初始化时才是声明。
单定义规则(变量只能有一次定义,与赋值区分开):如果要在多个文件中使用外部变量,只需要在一个文件中包含其定义,但是在使用其他变量的所有文件中,使用extern进行声明。::warming;使用域作用符表示使用全局变量。
静态持续性,内部链接性:
如果一个文件中定义了外部变量,另一个文件意图定义一个相同名称的内部变量,那么应该使用static 关键字来说明是内部链接性。
无链接性的局部变量:,将static限定符用于在代码块中定义的变量,即在改代码块不活动的时候也存在。因此在两次函数调用之间,静态局部变量的值将保持不变。程序只初始化一次静态局部变量。
3.说明符和限定符
volatile关键字表明,假设编译器发现程序在几条语句中两次使用了某个变量,则编译器可能不是查找这个值两次而是将这个值缓存到寄存器里。Volatile关键字相当于不要进行这种优化。
Mutable:将其使用在结构或者类内的变量可以实现,即使结构或者类变量为const,其某个成员也可以被修改。
Const:注意在c++看来,默认情况下全局变量的链接性为外部的,但const全局变量的链接性为内部的。即全局const定义就像使用了static说明符一样。但是可以使用extern关键字覆盖默认的内部链接性。extern const int states=50;必须在所有使用该常量的文件中使用extern关键字来声明它。另外注意,单个const在多个文件中共享,只有一个文件可以对其进行初始化。
函数和链接性:
所有函数的存储持续性都自动为静态的,在整个程序执行期间存在。默认函数具有外部链接性可在文件之间共享。可在函数原型中使用extern指出函数是在另一个文件中定义的。也可以使用static将函数设置为内部使之只能在一个文件中使用。必须同时在函数原型和函数定义中都使用static。单定义规则也适用于非内联函数。C++执行名称修饰,名称名称矫正对重载函数生成不同的符号名称(因为c++允许重名函数)。
extern “C” void spiff(int);//使用C链接性
extern “C++” void spiff(int);//显示使用c++链接
extern void spaff(int);//隐式使用c++链接
动态分配:
动态内存由运算符new和delete控制,而不是由作用域和链接性规则控制。因此允许在一个函数中分配动态内存,在另一个函数中释放。通常编译器使用三块独立的内存:一块用于静态变量,一块用于自动变量,一块用于动态存储。虽然存储方案概念不适用于动态内存,但适用于用来跟踪动态内存的自动和静态指针变量。如:
float *p_fees=new float[20];由new分配的内存一直保留在内存中,直到使用delete释放。但当包含该语句的代码块执行完时,p_fees指针将消失。如果另一个函数还要使用这80个字节的内容,那么必须将其地址传递或者返回给函数。若将p_fees声明为外部的则文件中位于该声明后后面的所有函数都可以使用它。在另一个文件中extern float *p_fees;声明则可以使用该指针。
New运算符初始化:
基本类型: int *pi=new int (6); double * pd=new double (99.99);
初始化常规结构或者数组需使用大括号的列表初始化(支持c++11):
Struct where {double x; double y; double z};
Where *one =new where {2.5,5.3,7.2};
Int *ar=new int[4] {2,4,6,7};
New失败时,引发std::bad_alloc异常。
New:运算符、函数和替换函数
Void *operator new(std::size_t);
Void *operator new[](std::size_t);
这些被称为分配函数位于全局名称空间中。也有由delete和delete[]调用的释放函数。
Void operator delete(void *);
Void operator delete[](void *);
定位new运算符:new还有一种变体被称为定位new运算符,用以指定要使用的位置。需要#include
假设p1是double 指针,如果使用定位new运算符时候,而buffer是char数组,那么需要
(void *)buffer强制转换。注意定位new运算符使用传递给它的地址,它不跟踪哪些内存单元已经被使用,也不查找未使用的内存块,所以如果不想覆盖旧数据,在下次写入的时候应该使用偏移量来申请新的内存:pd2=new (buffer +N*sizeof(double)) double[N];
Delete运算符只能用于这样的指针:指向常规new运算符分配的堆内存。即此时定位new分配在静态内存中,所以不能用delete删除。
4.名称空间
声明区域:可在其中进行声明的区域。
潜在作用域:变脸从声明点开始到其声明区域的结尾。
作用域:变量对程序而言可见的范围被称为作用域。
新的名称空间:通过定义一种新的声明区域来创建命名的名称空间。使用关键字namespace
Namespace jack
{
Double pail;
Void fetch();
Struct hill();
}
名称空间可以是全局的(外部的),也可以位于另一个名称空间中,但不能位于代码块中。可以在该文件或者另外的文件中使用jack的名称空间为fetch的代码。使用域作用夫::,如
Jack::pail叫限定的名称。
注意:假设名称空间和声明区域定义了相同的名称,如果试图使用using声明将名称空间的名称导入该声明区域,则这两个名称将发生冲突而导致错误。如果使用using编译指令将该名称空间的名称导入该声明区域,则局部版本将隐藏名称空间的版本。
Char fetch;
Int main()
{
Using namespace jack;
Double fetch; //此时fetch就是局部版本的fetch。
}
Int main()
{
Using jack::fetch;
Double fetch;//错误,此时已经有fetch的定义了。
}
名称空间其他特性:
Namespace elements
{
Namespace fire
{
Int flame;
}
Float water;
}
此时flame是using namespace elements::flame;也可以在名称空间中声明和编译其他名称空间的名称。Using编译指令是可以传递的,A>B,B>C,那么A>C;即会导入一个名称空间以及其包含的名称空间。也可以给名称空间创建别名,namewpace mvft=my_very_favorate_things; 未命名的名称空间相当于static变量,相当于链接性为内部的静态变量。
在做多文件工程项目时,头文件中声明名称空间,常量、结构定义和函数原型都可以加入其中。一个源文件用来完成位于名称空间中的函数定义,且必须在其对应的名称空间中定义。还需要一个源文件代码,使用这些函数、结构和函数原型。
//+++++++++++++++++++++++9_1.hpp++++++++++++++++++++++++++
#include
using namespace std;
const int len=40;
const int input=50;
struct golf
{
char fullname[len];
int handicap;
};
void setgolf(golf &g,const char *name,int hc);
int setgolf(golf &g);
void handicap(golf &g, int hc);
void showgolf(const golf &g);
//+++++++++++++++++++++++++9_1.cpp+++++++++++++++++++++++++++
#include
#include
#include"9_1.hpp"
void setgolf(golf &g,const char *name,int hc)
{
strcpy(g.fullname,name);
g.handicap=hc;
}
int setgolf(golf &g)
{
char a[len];
int b;
int bo=1;
cout<<"enter your name"<>b;
cin.get();
strcpy(g.fullname,a);
g.handicap=b;
return bo;
}
void handicap(golf &g,int hc)
{
g.handicap=hc;
}
void showgolf(const golf &g)
{
cout<<"your input name is"<
#include"9_1.hpp"
int main()
{
golf ann;
setgolf(ann,"Ann Birdfree",24);
showgolf(ann);
golf pt[input];
for(int i=0;i
#include
using std::string;
const int arsize=10;
void strcount(string);
int main()
{
using namespace std;
string input;
cout<<"enter a line\n";
getline(cin,input);
while(cin)
{
if(input=="")
break;
strcount(input);
cout<<"enter nest line"<
#include
#include
using namespace std;
const int size =2;
struct chaff
{
char dross[20];
int slag;
};
char buffer[1024];
int main()
{
chaff *p1=new (buffer) chaff[size];
char *p2=new char[1024];
chaff *p3=new (p2) chaff[size];
char input_dross[20];
int input_slag;
for(int i=0;i>input_slag;
cin.get();
p1[i].slag=input_slag;
p3[i].slag=input_slag;
cout<<"p1's information are "<
namespace SALES
{
const int quarters=4;
struct sales
{
double sale[quarters];
double average;
double max;
double min;
};
void setsales(sales &s,const double ar[],int n);
void setsales(sales &s);
void showsales(const sales &s);
}
//++++++++++++++++++++++++9_4.cpp+++++++++++++++++++++++++++++++++
#include
#include"9_4.hpp"
namespace SALES
{
using std::cin;
using std::cout;
using std::endl;
void setsales(sales &s,const double ar[],int n)
{
double total=0;
for(int i=0;is.sale[j])
min=s.sale[j];
}
s.average=total/n;
s.max=max;
s.min=min;
}
void setsales(sales &s)
{
double arr;
int number;
cout<<" enter your numbers"<>number;
cin.get();
for(int i=0;i>arr;
cin.get();
s.sale[i]=arr;
}
double total=0;
for(int k=0;ks.sale[j])
min=s.sale[j];
}
s.average=total/number;
s.max=max;
s.min=min;
}
void showsales(const sales &s)
{
double number=sizeof(s.sale)/sizeof(double);
for(int i=0;i
#include"9_4.hpp"
int main()
{
using SALES::sales;
sales sa;
sales sb;
double a[4]={1,2,3,4};
setsales(sa,a,4);
showsales(sa);
setsales(sb);
showsales(sb);
return 0;
}