T:2019/11/28 W:四 17:0:11 [HTML]: @TOC
关键字
static 关键字
- 可以分为如下五种类型
- 全局静态变量
- 在全局变量前加上关键字static,全局变量就定义成一个全局静态变量.
- 内存中的位置:静态存储区,在整个程序运行期间一直存在。
- 初始化:未经初始化的全局静态变量会被自动初始化为0(对于自动对象,如果没有显示初始化,会调用零参数构造函数,如不存在则编译失败);
- 作用域:全局静态变量在声明他的文件之外是不可见的,准确地说是从定义之处开始,到文件结尾。
- 局部静态变量
- 在局部变量之前加上关键字
static
,局部变量就成为一个局部静态变量。 - 内存中的位置:静态存储区
- 初始化:未经初始化的全局静态变量会被自动初始化为
0
(对于自动对象,如果没有显示初始化,会调用零参数构造函数,如不存在则编译失败); - 作用域:作用域仍为局部作用域,
- 当定义它的函数或者语句块结束的时候,作用域结束。
- 但是当局部静态变量离开作用域后,并没有销毁,而是仍然驻留在内存当中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变;
C语言
中局部静态变量不能使用变量进行初始化的, 因为它在编译期间就被编译器初始化了.C++
因为引入了对象,必须调用构造函数,所以编译器把对局部静态变量的初始化推迟至第一次调用之前,也就是可以使用变量进行初始化.
3. 静态函数 - 在函数返回类型前加static,函数就定义为静态函数。**函数的定义和声明在默认情况下都是extern的**,但**静态函数只是在声明他的文件当中可见,不能被其他文件所用**。 - 函数的实现使用static修饰,**那么这个函数只可在本cpp内使用,不会同其他cpp中的同名函数引起冲突;** - warning:不要再头文件中声明static的全局函数,不要在cpp内声明非static的全局函数,如果你要在多个cpp中复用该函数,就把它的声明提到头文件里去,否则cpp内部声明需加上static修饰; 4. 类的静态成员 - 在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。 - 因此,**静态成员是类的所有对象中共享的成员,而不是某个对象的成员**。对多个对象来说,静态数据成员只存储一处,供所有对象共用 5. 类的静态函数 - **静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员**。因此,对静态成员的引用不需要用对象名。 - **在静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员(这点非常重要)**。*如果静态成员函数中要引用非静态成员时,可通过对象来引用。从中可看出,调用静态成员函数使用如下格式:<类名>::<静态成员函数名>(<参数表>);* - 不能被virtual修饰,静态成员函数没有this 指针,虚函数的实现是为每一个对象分配一个vptr 指针,而vptr 是通过this 指针调用的,所以不能为virtual;虚函数的调用关系,this->vptr->ctable->virtual function
- 说一下static关键字的作用
const 关键字
- 一般可以分为如下六种类型
const
变量: 表明标了为const
类型, 通常需要被初始化否则后面将不能被修改, 对该变量的修改操作都会被编译器阻止.const
指针对象: 标明该指针为普通的左值类型可以进行修改, 但是不能通过该变量修改做指向的对象, 则通过该指针只能访问const
类型的成员函数.const
引用: 它所绑定的对象不能被修改const
形参: 和普通的实参分类一样分为const
变量,const
指针对象,const
引用, 作用也类似,表示不能修改该变量.const
返回值: 通常是为了表明返回值是一个const类型防止返回值被修改, 或则被当做左值放在赋值运算的左边const
成员函数: 是指成员函数不会修改类对象的任何成员变量, 如果返回值为对象成员的引用则必须返回const
引用, 同时const
成员函数不能调用非const
函数, 其主要是因为const
成员函数所持有的this
指针是一个const
类型的指针, 因为不能调用非const
类型的成员函数,
- 7、const?
malloc/free, new/delete 和 new[]/delete[]
malloc/free
,new/delete
的区别- 首先:
new/delete
是c++
关键字, 而malloc/free
是c语言
的库函数 - 其次:
malloc/free
需要指明申请的空间的大小, 且并不负责构造和析构new/delete
是在malloc/free
上增加了一层封装, 他们根据类的确定所需要的内存大小并调用malloc
分配空间, 还负责调用构造和析构函数
- 首先:
- 因此可以看出:
new operator
可以分解operator new
和placement new
两个动作,是operator new
和placement new
的结合。
- 与
new
对应的delete
没有placement delete
语法- 它只有两种,分别是
delete operator
和operator delete
。 delete operator
和new operator
对应,完成析构对象和释放内存的操作。- 而
operator delete
只是用于内存的释放,与C语言
中的free
相似。
- 它只有两种,分别是
new/delete
和new[]/delete[]
的区别new/delete
new
- 简单类型直接调用
operator new
分配内存; - 对于复杂结构,先调用
operator new
分配内存,然后调用placement new
在分配的内存上调用构造函数;
- 简单类型直接调用
delete
- 简单数据类型默认只是调用
operator delete
释放内存; - 复杂数据类型先调用
析构函数
再调用operator delete
;
- 简单数据类型默认只是调用
new[]/delete[]
new[]
- 对于简单类型,
new[]
计算好大小后调用operator new
; - 对于复杂数据结构
AA* P = new AA[10];
new[]
先调用operator new[]
分配内存, 分配内存时多分配四个字节用于存放元素个数., 返回地址为p
p
的最开始的4
个字节用于存放元素个数n
, 然后从调用n
次构造函数从p-4
开始构造对象.- 返回地址,也就是
P
, 即为p-4
- 对于简单类型,
delete[]
- 对于简单类型, 直接调用
operator delete
进行释放(注意简单类型并没有利用4
个字节保存元素个数, 由编译器自行优化) - 对于复制类型,
- 首先将指针前移
4
个字节获得元素个数n
, 然后执行n
次析构函数, 最后并释放掉内存. - 因为指针指向的是
p-4
并不是内存的起始地址, 所以使用delete
将无法完成释放, 因为free
需要通过起始地址进行释放, 而p-4
不是起始地址
- 首先将指针前移
- 对于简单类型, 直接调用
- new和delete的实现原理,delete[]是如何知道释放内存的大小的
extern “c”
C++
调用C函数需要extern C
, 因为C语言
没有函数重载- 由于
C++
支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中, 而不仅仅是函数名; - 而
C语言
并不支持函数重载,因此编译C语言
代码的函数时不会带上函数的参数类型,一般之包括函数名。
- 由于
- 说一下
extern "C"
四种cast转换
C++
中四种类型转换是:static_cast
,dynamic_cast
,const_cast
,reinterpret_cast
const_cast
:
- 用来移除
const
或volatile
属性。但需要特别注意的是const_cast
不是用于去除变量的常量性,而是去除指向常数对象的指针或引用的常量性,其去除常量性的对象必须为指针或引用。- 如果对一个指向常量的指针,通过
const_cast
移除const
属性, 然后进行修改, 编译通过,但是运行时会报段错误static_cast
: 静态类型转换(不能移除const/volatile
属性)是最常看到的类型转换, 几个功能.
- 内置类型之间的转换, 精度耗损需要有程序员把握
- 继承体系中的上下行转换(上行:子类转父类,安全转换; 下行:父类转子类, 不安全转换)
- 指针类型转换:
空指针转换成目标类型的空指针
,把任何类型转换成void 类型
。dynamic_cast
: 主要用在继承体系中的安全向下转型
- 它能安全地将指向基类的
指针/引用
转型为指向子类的指针/引用
, 转型失败会返回null
(转型对象为指针时)或抛出异常bad_cast
(转型对象为引用时)。dynamic_cast
会利用运行时的信息(RTTI)
来进行动态类型检查,因此dynamic_cast 存在一定的效率损失。- 而且
dynamic_cast
进行动态类型检查时, 利用了虚表中的信息, 所以只能用于函数虚函数的类对象中.reinterpret_cast
强制类型转换,非常不安全
++i 和 ++i
- ++i 和 ++i的区别
++i
先自增1, 再返回. 函数定义式无形参, 实现过程中不会有临时变量生成,i++
先返回i
,再增加1. 函数定义式有一形参, 实现过程中由于需要返回自增前的量,所以需要一个临时变量.
- ++i 和 ++i的区别
- ++i 和 i++的实现
class, union, struct
struct
在C
和C++
中是不同的C
语言中:struct
为自定义数据类型, 结构体名不能单独作为类型使用, 其结构名前必须加struct
才行struct
为变量的集合, 不能存定义函数(但是可以存在函数指针变量)struct
不存在访问权限控制的概念
C++
中:struct
为抽象数据类型, 只一个特殊的class
, 支持成员函数的定义, 可以继承和实现多态- 增加了访问权限控制的概念, 但是默认访问和继承权限为
public
- 结构体名字可以为直接做为类型使用
C++
中struct
和class
的区别- 默认的访问和继承权限不同
- 注意
C++
中struct
可以使用模板
union
联合C语言
中:union
是一种数据格式,能够存储不同的数据类型,但只能同时存储其中的一种类型。union
的数据成员是共享内存的, 以成员最大的做为结构体的大小- 每个数据成员在内存中的起始地址是相同的。
C++
中:union
结构式一种特殊的类。 默认访问权限是public
。- 能包含访问权限、成员变量、成员函数(可以包含构造函数和析构函数)。
- 不能包含虚函数和静态数据变量。也不能被用作其他类的基类,它本身也不能从某个基类派生而来。
union
成员是共享内存的,以size
最大的结构作为自己的大小。- 每个数据成员在内存中的起始地址是相同的。
- 无论是
C/C++
,union
的储存方式都是小端模式储存的
- class、union、struct 的区别
mutable
- 通常情况下
const
成员函数时不能被类对象的成员变量的, 但是可以修改被mutable
修饰的成员变量- 通常我们任务
mutable
位类的辅助状态, 只是类的一些表诉功能, 修改它不会改变对象的状态 - 通常我们可以是用
const_cast
在const
成员函数中修改所有的成员变量
- mutable
volatile
volatile
关键字是一种类型修饰符,被它修饰的变量拥有三大特性: 易变性, 不可优化性, 顺序性- 易变性: 编译器对
valatile
的访问总是从内存中读取数据, 即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。 - 不可优化性:
volatile
告诉编译器,不要对我这个变量进行各种激进的优化,甚至将变量直接消除,保证程序员写在代码中的指令,一定会被执行。 - 顺序性: 保证
Volatile
变量间的顺序性,编译器不会进行乱序优化。但是可能会被CPU优化
- 易变性: 编译器对
- 声明时语法:
int volatile vInt
; volatile
用在如下的几个地方: 1) 中断服务程序中修改的供其它程序检测的变量需要加volatile
; 2) 多任务环境下各任务间共享的标志应该加volatile
; 3) 存储器映射的硬件寄存器通常也要加volatile
说明,因为每次对它的读写都可能由不同意义;- 113、
volatile
关键字的作用?
define, const, typedef, inline
const
与#define
的区别:作用阶段不同
,功能不同
,define作用丰富
,占用的空间不同
,作用域
- 作用阶段不同:
const
在编译和链接阶段其作用,define
在预编译阶段起作用 - 功能不同:
const
是定义一个变量, 拥有数据类型, 会进行语义语法检查define
是宏定义, 简单的问题替代, 没有类型检查
define
的作用更丰富:define
可以配合条件预编译指令, 完成特殊的逻辑, 例如防止重复引用文件- 编译后占用的空间:
const
定义的是变量, 会储存在数据段空间,define
是宏替换, 其值会储存在代码段 - 作用域不同:
define
没有作用域限制, 而const定义的变量通常有作用域的限制(全局变量默认为extern)
- 作用阶段不同:
-
#define
和别名typedef
的区别 1) 执行时间不同,typedef
在编译阶段有效,typedef
有类型检查的功能;#define
是宏定义,发生在预处理阶段,不进行类型检查; 1) 功能差异,typedef
用来定义类型的别名,定义与平台无关的数据类型,与struct
的结合使用等。#define
不只是可以为类型取别名,还可以定义常量、变量、编译开关等。 1) 作用域不同,#define
没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用。而typedef
有自己的作用域。 define
与inline
的区别 1)#define
是关键字,inline
是函数; 1) 宏定义在预处理阶段进行文本替换,inline
函数在编译阶段进行替换; 1)inline
函数有类型检查,相比宏定义比较安全;- 81、
define
、const
、typedef
、inline
使用方法?
ifdef, endif
- 从源文件到可执行程序的过程, 通常要经历:
预编译
,编译
,汇编
,链接
等过程 ifdef
,endif
为条件预编译指令, 生效于预编译阶段, 根据条件可以完成一些特殊的逻辑, 例如防止文件重复引用#ifdef
,#else
,#endif
为完整的逻辑, 分别表示, 如果定义了某个标识符, 则编译后续程序段, 否则编译另外一个程序段- 因为预编译阶段处于编译链的第一阶段, 它可以直接影响应用程序的大小.
- 96、说一下理解
ifdef
endif