内存模型和名称空间
2020-02-24 10:37:50 6 举报
AI智能生成
C++ plus
作者其他创作
大纲/内容
名称空间//331后未看
问题
名称冲突增加,很多程序的名称是一样的。
作用
控制名称的作用域
传统的C++名称空间
相关概念
声明区域
含义
可以在其中声明的区域。
在函数外面声明全局变量——声明区域为其声明所在文件
函数中——代码块
在函数外面声明全局变量——声明区域为其声明所在文件
函数中——代码块
潜在作用域
含义
声明点——声明区域结尾
作用域
含义
变量对程序可见的范围
有时全局变量会被局部变量隐藏
新的名称空间特性
新增的功能
通过定义一种新的声明区域来创建命名的名称空间
好处
名称空间之间相同的变量名称不会冲突
允许程序的其他部分使用该名称空间中声明的东西
创建名称空间
格式
namespace Jack{
double pail; //变量
void fetch{};//函数
int pal;
struct Well{....};//结构
}
double pail; //变量
void fetch{};//函数
int pal;
struct Well{....};//结构
}
没有分号
说明
名称空间可以全局,也可以位于另个一个名称空间中,
不能位于代码块中//??
所以,名称空间中声明的名称链接性是外部(除非它定义了常量//???)
不能位于代码块中//??
所以,名称空间中声明的名称链接性是外部(除非它定义了常量//???)
不能位于代码块中,意思是不能位于函数中
名称空间类型
用户自定义
全局名称空间
对应于文件级声明区域,
前面所说的全局变量在全局名称空间中
前面所说的全局变量在全局名称空间中
???什么东西 ,那std 是什么名称空间
加入新名称
格式
namespace Jack{
char * goose(const char *);
}
char * goose(const char *);
}
在那里定义名称空间?哪里加入新的名称??这样加入不会乱吗??
因为名称空间开放性,可以在后续加入。
函数定义
在Jack中 有fetch函数,在Jack中提供函数代码
namespace Jack{
void fetch()
{
......
}
}
namespace Jack{
void fetch()
{
......
}
}
没有分号,在名称空间中提供的函数要
写函数地冠以
写函数地冠以
访问名称空间的名称
方法
作用域解析
::运算符
::运算符
Jack::pail
using 声明
using Jack::pail;
using 编译指令
using namespace Jack;
说明
用using 声明,using编译指令会增加名称冲突性
using Jack::pal;
using Jill::pal;
pal = 4; //not allow
using Jill::pal;
pal = 4; //not allow
using声明
位置
局部引用名称空间的名称
namespace Jill{
....
double fetch
}
char fetch;
int main()
{
using Jill::fetch;
double fetch; //不允许
cin>>fetch; //隐藏全局变量fetch
cin>>::fetch;//引用全局变量
}
....
double fetch
}
char fetch;
int main()
{
using Jill::fetch;
double fetch; //不允许
cin>>fetch; //隐藏全局变量fetch
cin>>::fetch;//引用全局变量
}
说明
::pail表示用全局名称空间的全局变量
全局引用
using Jill::fetch;//在以后函数中,fetch都有作用
int main
{
....
}
int main
{
....
}
using编译
位置
局部引用名称空间的名称
namespace Jill{
....
double fetch
}
char fetch;
int main()
{
using namespace Jill;
double fetch; //允许
cin>>fetch; //隐藏全局变量fetch,Jill中的fetch
cin>>Jill::fetch;
cin>>::fetch;//引用全局变量
}
....
double fetch
}
char fetch;
int main()
{
using namespace Jill;
double fetch; //允许
cin>>fetch; //隐藏全局变量fetch,Jill中的fetch
cin>>Jill::fetch;
cin>>::fetch;//引用全局变量
}
子主题
using 声明,编译的区别
声明
using Jill::fetch;
double fetch;//not allow
double fetch;//not allow
double fetch;
using Jill::fetch; //not allow
using Jill::fetch; //not allow
编译
using namespace Jill;
double fetch;//可以,局部隐藏名称版本
double fetch;//可以,局部隐藏名称版本
可以
使用声明
原因
原因
声明比编译指令更加安全。如果名称与局部名称发生冲突,会报错。
名称空开放性,使名称空间的名称可能分散在各处
尽量使用 ::运算符和声明
其他
include<iostream.h>可代替 include<iostream> using namespace std;
iostream.h没有使用名称空间std
名称空间特性
开放性
嵌套性
名称空间中创建
名称空间
名称空间
例子
namespace elements
{
namespce fire
{
int flame;
}
}
{
namespce fire
{
int flame;
}
}
访问嵌套的元素
flame
elements::fire::flame
只能这样写吗???
using elements::fire::flame;
flame直接用
flame直接用
using namespce elements::fire;
flame (直接用)
flame (直接用)
名称空间中
使用using 编译
指令,声明
使用using 编译
指令,声明
例子
namespace myth
{
using Jill::fetch;
using namespace elements;
using std::cout;
uing std::cin;
}
{
using Jill::fetch;
using namespace elements;
using std::cout;
uing std::cin;
}
访问嵌套的元素
fetch
myth::fetch
Jill::fetch
using namespce myth;
fetch;
fetch;
可传递
含义
using namespace myth
=
using namespace myth;
using namespace elements;
=
using namespace myth;
using namespace elements;
创建名称空间别名
格式
namespace myth{。。。}是名称空间
namespce m=myth;
给在名称空间定义的
名称空间改别名
名称空间改别名
namespce MEF=myth::elements::fires;
未命名的名称空间
格式
namespace
{
int ice;
...
}
{
int ice;
...
}
作用
代替链接性为内部的静态变量
static int counts;
int other();
int main()
{
...
}
int other()
{
...
}
int other();
int main()
{
...
}
int other()
{
...
}
namespace
{
int counts;
}
int other();
int main()
{
...
}
int other()
{
...
}
{
int counts;
}
int other();
int main()
{
...
}
int other()
{
...
}
有名称的要使用using ...???
名称空间实例
p331程序9.11
好好揣摩。
名称空间的作用
第二个源代码文件可以不是放main ,
只放名称空间的函数定义。在头文件中,
名称空间只能放函数原型?
只放名称空间的函数定义。在头文件中,
名称空间只能放函数原型?
一定要导入名称空间最初的创建才可以吗?
名称空间及其前途
p334
单独编译
含义
将组件函数放在独立的文件中。单独编译这些文件,
将它们链接成可执行的程序
将它们链接成可执行的程序
翻译单元 就是 文件
好处
管理便捷
程序分成三个文件
头文件
一般包含的内容
函数原型
使用#define 或 const 定义的符号常量
结构声明
类声明
模板声明
内联函数
代码
代码
子主题
说明
包含头文件时候应该用"coordin.h"不是<coordin.h>
""
编译器首先查找当前的工作目录或源代码目录(或其他,取决于编译器)。
如果没有找到头文件,再标准位置查找
如果没有找到头文件,再标准位置查找
<>
编译器在存储标准头文件的主机系统的文件系统中查找
一个文件只能包含同一个头文件一次,但是可能使用另一个 包含了同一个头文件 的头文件。
用基于预处理编译指令#ifndef。
#ifndef COORDIN_H_
...
#endif
意味仅当以前没有使用预处理器编译指令#define 定义名称 COORDIN_H_时,才处理#ifndef 和#endif之间的语句
(#ifndef COORDIN_H_ 下面的 #define COORDIN_H_ 的作用是这样)
用基于预处理编译指令#ifndef。
#ifndef COORDIN_H_
...
#endif
意味仅当以前没有使用预处理器编译指令#define 定义名称 COORDIN_H_时,才处理#ifndef 和#endif之间的语句
(#ifndef COORDIN_H_ 下面的 #define COORDIN_H_ 的作用是这样)
注意
不要把函数定义和变量声明放在头文件中
源代码文件
内容
包含与结构有关的函数的代码
一般只是一个main函数
代码
代码
说明
注意
如果只有源代码文件1,2没有头文件。需要在两个文件前部都加上头文件的的东西。
例如都要加上同一个函数原型
例如都要加上同一个函数原型
组合原理
书p302
怎么在一起编译链接???
执行编译命令??
注意
不同编译器有不同的名称修饰。要确保在链接时,
所有文件对象或库都是由同一个编译器生成
所有文件对象或库都是由同一个编译器生成
存储持续性、作用域和链接性
存储数据方式
持续性
自动存储持续性
有两种变量
静态存储持续性
3种变量
线程存储持续性
动态存储持续性
作用域
链接性
总结(图)
位置 | 持续性 | 链接性 | 作用域 |
函数定义内声明的变量 (自动变量)(关键字auto) | 自动 | 无链接性 | 局部 |
函数定义内,并且使用static声明 | 静态 | 无链接性 | 局部 |
函数定义外,并且使用static声明(或const) | 静态 | 内部 | 全局 |
函数定义外声明 (外部变量) | 静态 | 外部 | 全局 |
new创建的变量 | 动态 |
作用域和链接
作用域
描述
名称在文件的多大范围可见
变量类型
局部变量
作用域
只在代码块内起作用
定义方式
在函数中定义的变量
变量类型
变量是静态,或者自动变量
自动变量只能是局部变量。
全局变量
作用域
在定义它的位置,到文件结束
定义方式
在函数外部定义的变量
变量类型
变量是静态变量
其它类型作用域
在类中声明的成员的作用于为整个类。
名称空间中声明的变量的作用域为整个名称空间
全局作用域是名称空间作用域的特例???
函数的作用域可以是整个类 或整个名称空间(包括全局的)
链接性
描述
名称如何在不同的单元(文件)间共享
链接为外部的名称——在文件间共享
如果在其他文件中再次引用变量,要再次引用声明,格式:extern int a;(不能初始化)
链接为内部的名称——只在一个文件中共享,要加static
注意
自动变量没有链接性
自动存储持续性
声明位置
在函数中声明
在函数中声明的变量默认为自动变量,局部变量
注意
在函数中的代码块内
和非代码块内同时声
明同一个变量
和非代码块内同时声
明同一个变量
{
int teledeli;
...
....
{
int teledeli; //外部的teledeli被隐藏
...
}
...
...
}
作用域
局部变量
整个函数中
代码块内
如果变量在函数中的代码块中声明
链接性
无链接性
自动变量和栈
自动变量
管理方法
管理方法
留出一段内存,视为栈,管理变量的增减
栈的原理
书p308图
说明
只是移动指针的位置,没有删除内存中的数据
寄存器变量
register int count_fast
说明
register在C++11不表示变量使用CPU寄存器来存储自动变量
在C++11 register的意思是,变量为自动变量 ,与原来的auto用法完全相同
保留关键字register原因,避免使用了该关键字的现有代码非法
静态持续变量
连接性类型
外部~
内部~
无链接性
持续性
整个程序执行期间存在,不论是有无链接性,链接性是什么
存储位置
分配固定的内存块存储所用的静态变量
创建
链接性外部
int global = 100;
int main()
{
.......
}
int main()
{
.......
}
简称外部变量
链接性内部
static int global = 100;
int main()
{
...
}
int main()
{
...
}
无链接性
int main()
{
static n=100;
}
{
static n=100;
}
子主题
说明
自动初始化为0或者'\0'
静态变量初始化
知识
静态初始化在编译器处理文件(翻译单元)时初始化变量
类型
0初始化和常量表达式初始化——静态初始化
例子
int x;
int y = 5;
long z = 13*13;
int y = 5;
long z = 13*13;
静态初始化可以适用于自动变量吗??
动态初始化在编译后初始化
例子
const double pi = 4.0*atan( 1.0 );
静态持续性,外部链接性
变量特点
链接性为外部的变量称为外部变量。也是全局变量。
在不同文件中可以共享,但是要有一定的格式说明共享。
在不同文件中可以共享,但是要有一定的格式说明共享。
单定义规则
含义
变量只能有一次定义//??
程序中可包含多个同名变量,但是每个变量都只有一个定义。
//但是不能同时定义同一名称的外部变量?
//一个全局,一个局部可以。
//两个都是局部,但处于不同代码块可以。
//但是不能同时定义同一名称的外部变量?
//一个全局,一个局部可以。
//两个都是局部,但处于不同代码块可以。
如果一个程序包含两个文件,在两个文件中都要用到同一个外部变量Num,不能分别在两个文件中各自定义一个外部变量Num,这样会引发“重复定义”错误
变量声明方式
定义声明
简称定义
特点
给变量分配存储空间
格式
引用声明
简称声明
特点
不给变量分配存储空间,引用的是已有的变量//??
好像与以前的不同,定义声明在开头??引用声明在后面??
格式
extern int cats;
说明
有extern 且不能初始化。虽然要把file01 fle02链接在一起,
但在file02中使用cats ,也要引用声明后才能用
但在file02中使用cats ,也要引用声明后才能用
//file01.cpp
extern int cats =20; //有extern 但是初始化了,是定义声明,分配存储空间。
int dogs = 22 ;//定义声明
int flease; //定义声明
//file02.cpp
extern int cats; // 有extern ,是引用声明,用的是file01中的cats
extern int dogs;
extern int cats =20; //有extern 但是初始化了,是定义声明,分配存储空间。
int dogs = 22 ;//定义声明
int flease; //定义声明
//file02.cpp
extern int cats; // 有extern ,是引用声明,用的是file01中的cats
extern int dogs;
如果在file02.cpp中让extern int cats = 30 会怎么办?好像会报错,不能定义两次。
在file02中,不可以使用flease,因为没有引用声明
在file02.cpp中引用了01中的cats ,在02中修改cats,01中的cats会相应变化,因为是同一个变量。
外部变量与
局部变量
局部变量
如果cats是全局变量,在函数中也定义了一个cats 的局部变量。在函数中会先隐藏全局的cats,
如果要显示全局的cats ,在cats前+::,::cats 即可。
如果要显示全局的cats ,在cats前+::,::cats 即可。
在程序中过多使用全局变量会导致程序不可靠。造成数据的不必要访问,数据完整性可能会被破坏,所以必要时加上const 定义全局变量。(其实是字符常量)
静态持续性、内部链接性
变量特点
只在所属的一个文件中可共享
声明
函数外部,声明变量加static int n;
说明
//file1
int errors= 5
...
//file2
int errors =5
int errors= 5
...
//file2
int errors =5
不可以,违反单定义规则。
最好在编f2的时候,error 的声明 : extern int errors; statics int errors;
最好在编f2的时候,error 的声明 : extern int errors; statics int errors;
//file1
int erroes =5
...
//file2
static int errors =5
int erroes =5
...
//file2
static int errors =5
可以,静态内部变量隐藏常规的外部变量。
静态连续性,无链接性
变量特点
变量时局部变量。只在代码块中可用,但是在代码块不处于活动状态时扔存在。
如果变量是初始化,只会初始化一次。
再次进入函数时,变量的值保持不变。忽视初始化。
声明
在函数中
{
static int n = 2;
...
}
{
static int n = 2;
...
}
其它
cin.get( char * ,int )读取空行,cin会是false。
int a[n]
cin.get(a ,int n )读取一行时,都不够数,后面都会被置于\0?
如果满了 ,只读取n-1个数,最后一个设置成\0
int a[n]
cin.get(a ,int n )读取一行时,都不够数,后面都会被置于\0?
如果满了 ,只读取n-1个数,最后一个设置成\0
说明符和限定符
作用
提供了其它有关存储的信息
存储说明符
内容
auto
C++11中不再是说明符
register
C++11中含义只是说明变量时自动类型
static
extern
thread_local
C++11新增
指出变量的持续性与其所属线程的持续性相同
mutable
说明
只能有一个说明符,但是thread_local 变量除外,它可与static ,extern 结合
cv-限定符
内容
const
volatile
作用//??
即使程序没有对内存单元进行修改,其值也可能发生变化。书p317
mutable
作用
指出,即使结构(或类)变量为const,其某个成员也可以被修改
格式
struct student{
char name[20];
mutable double score;
}
const student std1={"Wang",99.0};
std1.score = 88//allow
char name[20];
mutable double score;
}
const student std1={"Wang",99.0};
std1.score = 88//allow
再谈const
对全局变量的影响
int fingers = 10;
外部变量
const int fingers = 10;
全局变量的链接性是内部的。
const int fingers = 10;可以放在头文件中,因为是内部,所以不用担心会重复定义。
const全局变量
声明为外部变量
声明为外部变量
extern const int fingers = 10;
不能放在头文件中。
函数和链接性
函数持续性
静态,整个程序执行期间都一直存在
链接性
默认为外部
可以在函数原型中使用关键字extern指出函数时在另一个文件中定义的。(不过这是可选的??)
在每个文件中,只要使用到函数,直接写上函数原型就可以。
内部链接性
方法
在函数原型和函数定义中使用关键字static
static int private(double x);
...
static int private(double x);
{
....
}
...
static int private(double x);
{
....
}
说明
函数只在本文件中适用。
可在其他文件定义同名函数(特征标也一样也可以?),这时会隐藏外部函数
注意
非内联函数定义只有一个。遵守单定义规则。
子主题
C++在哪里查找函数定义
函数是静态
在文件中查找
不是静态
在程序文件中查找(要包括头文件???吗??)找到两个定义,发出错误消息。
没有找到,在库中查找。
没有找到,在库中查找。
这样说,好像不用使用头文件。
使用某些头文件才能使用一些函数是怎么回事。
使用某些头文件才能使用一些函数是怎么回事。
如果定义了一个与库相同名字的函数,编译器将使用程序员定义的版本。
语言链接性
含义
问题
c语言链接性与c++语言链接性不同。
要在C++程序中使用C库中预编译的函数,产生有问题。
要在C++程序中使用C库中预编译的函数,产生有问题。
c语言函数名不能相同,C++允许函数重载
方法
在函数原型指出要使用的约定(c还是C++语言链接)
例子
extern "C" void spiff(int); //use C protocol for name look-up
extern void spiff(int);
默认C++
extern "C++" void spiff(int) //use C++
显式指出使用C++语言链接
存储方案和动态分配
编译器使用的内存
静态变量
自动变量
动态变量
动态内存
因素
由运算符new \delete 控制,不是由作用域和链接性控制
注意
float *p_free = new float [20] ;
当语句块执行完毕时候,p_free(自动变量)指针消失,
但在动态内存上(分配的动态内存)数据一直在内存上,
如果想在另一个函数使用动态内存上的数据,需要返回
p_free指向的地址。
当语句块执行完毕时候,p_free(自动变量)指针消失,
但在动态内存上(分配的动态内存)数据一直在内存上,
如果想在另一个函数使用动态内存上的数据,需要返回
p_free指向的地址。
其实没有动态变量这一说法。P_free是自动变量。给它分配的内存是动态内存。
当p_free 为外部变量的时候,位于该声明之后的函数都可以使用p_free,在其他文件中 :要 extern *p_free 引用声明一下
程序结束时候,由new释放的内存通常会被释放,但是有时也不一定。注意使用了new,就要用delete
new运算符初始化
单变量
float *f = new int (6.0);
括号
列表也可以(不过是C++11内容)
数组、结构
student *ptu = new student {2.5 , 3.5 ,4.5}
l列表C++11
int * ar = new int [4] {1,2,3,4 }
元素表示
ar[1] = 1
//把ar看成一个数组就可以
//把ar看成一个数组就可以
new失败
返回空指针,引发异常 std::bad_alloc
new:运算符、函数和替换函数
new 和new[]调用一下函数
分配函数
void * operator new(std::size_t);
std::size_t 是一个typedef,对应于合适的整数//??
void *operator new [ ] ( std::size_t );
释放函数
void operator delete(void *);
void operator delete [ ]( void *);
作用??表示的含义??
new 转换
//??
//??
int *pi = new int
int * pi = new( sizeof(int) )
int *pa = new int [40]
int *pa = new (40*sizeof(int))
delete pi;
delete (pi)
说明
用new 其实调用一个接受一个参数的new()函数
new delete 是可替换函数,可以自定义为new ,delete 提供替换函数。
定位new 运算符
new作用
在堆找到满足要求的内存块
被称为定位new运算符,能够指定要使用的位置
要求
头文件new
定位new工作原理
它只是返回传递给它的地址,并将其强制转化为void * ,以便可以赋给任何指针
格式
student * p1 = new (buffer1) student; #1
int *p2 = new (buffer2) int [20];
int *p2 = new (buffer2) int [20];
char buffer1[50];
char buffer2[120];
....
int main
{
student * p1 = new (buffer1) student;
int *p2 = new (buffer2) int [20];
}
char buffer2[120];
....
int main
{
student * p1 = new (buffer1) student;
int *p2 = new (buffer2) int [20];
}
int *p3 = new(buffer2 +N*sizeof(int )) int [20]#2
说明
#1 表明p1指向buffer1 的内存开头。就算buffer1 是char 类型 ,没有关系。
若int *p2 = new (buffer2) int [20] = {1,2,3,4,5...},
得到p2[0] = 1 ,p2[1] = 2 ,置于buffer1[ 0 ] = 多少就不知道了
得到p2[0] = 1 ,p2[1] = 2 ,置于buffer1[ 0 ] = 多少就不知道了
注意
delete 只能用于指向常规new 运算符分配的堆内存。
buffer 指定的内存是静态内存,所以不可以用
delete p2 ;会引发程序错误
buffer 指定的内存是静态内存,所以不可以用
delete p2 ;会引发程序错误
定位new其它形式
标准定位New 调用一个接受两个参数的new()函数
int * pi = new (buffer) int ;
int *pi = new( sizeof(int) ,buffer )
定位new函数不可以替换,但可以重载,即使额外的参数没有指定位置。//??
自由主题
0 条评论
下一页
为你推荐
查看更多