类型转换
概念
C 语言中的类型转换:
隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败; 显式类型转化:需要用户自己判断从什么类型转换为什么类型,转换格式为在需要转换的变量前加(要转换的类型)
; 为什么需要 C++ 中的类型转换:
隐式类型转化有些情况下可能会出问题:比如数据精度丢失; 显式类型转换将所有情况混合在一起,代码不够清晰; 因此 C++ 提出了自己的类型转化风格,注意因为 C++ 要兼容 C 语言,所以 C++ 中还可以使用 C 语言的转化风格;
C++ 的四种转换
static_cast
:static_cast
用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast
,但它不能用于两个不相关的类型进行转换;
int main ( ) {
double d = 12.34 ;
int a = static_cast < int > ( d) ;
cout<< a<< endl;
return0;
}
reinterpret_cast
:reinterpret_cast
操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型,主要功能为:reinterpret_cast
用在任意指针(或引用)类型之间的转换、以及指针与足够大的整数类型之间的转换、从整数类型(包括枚举类型)到无视大小的指针类型;
typedef void ( * FUNC) ( ) ;
int DoSomething ( int i) {
cout<< "DoSomething" << endl;
return 0 ;
}
void Test ( ) {
FUNC f = reinterpret_cast < FUNC> ( DoSomething ) ;
f ( ) ;
}
int main ( ) {
typedef int ( * FunctionPointer) ( int ) ;
int value = 21 ;
FunctionPointer funcP;
funcP = reinterpret_cast < FunctionPointer> ( & value) ;
funcP ( value) ;
}
const_cast
:const_cast
最常用的用途就是删除变量的const
属性,方便赋值;
void Test ( ) {
const int a = 2 ;
int * p = const_cast < int * > ( & a) ;
* p = 3 ;
cout<< a << endl;
}
dynamic_cast
:dynamic_cast
用于将一个父类对象的指针 / 引用转换为子类对象的指针 / 引用(动态转换); 向上转型:子类对象指针/引用 -> 父类指针/引用,这是切片操作,不需要转换,赋值兼容规则; 向下转型:父类对象指针/引用 -> 子类指针/引用,用dynamic_cast
转型是安全的,也可以使用static_cast
来进行转换,但是这是不安全的;
注意:
dynamic_cast
只能用于含有虚函数的类;dynamic_cast
会先检查是否能转换成功,能成功则转换,不能则返回 0;下面的代码中可以看出dynamic_cast
进行转换是安全的,static_cast
进行转换是不安全的,所以当dynamic_cast
进行转换时是会类型检查的,在父类向子类准换时,如果父类是用子类切片创建的,那么转换是安全的,如果父类是用自身创建的,那么dynamic_cast
会失败,而static_cast
成功;
class B {
public :
virtual int fun ( ) { }
} ;
class C : public B {
} ;
int main ( ) {
B* pb1 = new B;
C* pc1 = dynamic_cast < C* > ( pb1) ;
C* pc2 = static_cast < C* > ( pb1) ;
B* pb2 = new C;
pc1 = dynamic_cast < C* > ( pb2) ;
pc2 = static_cast < C* > ( pb2) ;
}
空间配置器
概念
空间配置器,顾名思义就是为各个容器高效的管理空间 (空间的申请与回收) 的,在默默地工作,虽然在常规使用 STL 时,可能用不到它,但站在学习研究的角度,学习它的实现原理对我们有很大的帮助;
作用
前面在自己模拟实现vector
、list
、map
、unordered_map
等容器时,所有需要空间的地方都是通过new
申请的,虽然代码可以正常运行,但是有以下不足之处:
空间申请与释放需要用户自己管理,容易造成内存泄漏; 频繁向系统申请小块内存块,容易造成内存碎片; 频繁向系统申请小块内存,影响程序运行效率; 直接使用malloc
与new
进行申请,每块空间都有额外空间浪费; 当申请空间失败时,我们无法很好地处理; 代码结构比较混乱,代码复用率不高; 未考虑线程安全问题; 在面对上面的问题时,我们可以使用空间配置器很好的解决,它是一种非常高效的内存管理机制,下面我们来看看空间配置器的原理吧!
原理
分类
SGI-STL 以 128 字节大小作为小块内存与大块内存的分界线,将空间配置器其分为两级结构,一级空间配置器处理大块内存(>= 128 字节),二级空间配置器处理小块内存(< 128 字节);
一级空间配置器
一级空间配置器原理非常简单,直接对malloc
与free
进行了封装,并增加了 C++ 中 set_new_handler 思想,底层代码我们就不看了,主要来介绍一下具体步骤;
先使用malloc
函数申请空间,申请成功则直接返回,申请失败则交由oom_malloc
来处理; 交由oom_malloc
来处理时,先检测用户是否设置了空间不足的对应措施;
如果没有设置对应措施,则抛异常; 如果设置了对应措施,则执行用户的对应措施,然后再次使用malloc
申请空间;
二级空间配置器
二级空间配置器专门负责处理小于 128 字节的小块内存,如何才能提升小块内存的申请与释放呢?SGISTL 采用了内存池的技术来提高申请空间的速度以及减少额外空间的浪费,采用哈希桶的方式来提高用户获取空间的速度与高效管理; 先进行前期处理,判断所需内存是否小于 128 字节;
大于 128:交由一级空间配置器申请; 小于 128:交由二级空间配置器申请; 如果用户所需内存不是 8 的整数倍,向上对齐到 8 的整数倍,假设此时所需的空间为 size; 根据所需内存 size 的大小,找到哈希表中对应的桶位置,判断该桶中是否存储了内存块;
有内存块:直接将桶中第一块内存返回给用户,然后结束 ; 无内存块:则需要向内存池申请空间; 向内存池申请空间要分两种情况讨论:
内存池剩余空间足够:
只申请了一块 size 大小的内存,直接将该块返回给用户,然后结束 ; 申请了多块 size 大小的内存,将一块内存返回给用户,剩余的挂在对应的哈希桶中,然后结束 ; 内存池剩余空间不够用户所需,如果此时内存池中还有剩余内存,需要先将内存池中剩余的内存挂到对应的哈希桶中,然后调用malloc
向系统申请空间: 调用malloc
向系统申请空间有两种情况:
申请成功,则将内存放入内存池,然后重新向内存池申请空间,然后结束 ; 申请失败,则从比 size 更大的哈希桶中找可以利用的内存:
如果找到更大的哈希桶,则将内存放入内存池中,然后重新向内存池申请空间,然后结束 ; 如果没有找到则调用一级空间配置器申请空间; 调用一级空间配置器申请空间分两种情况:
申请失败则抛异常,然后结束 ; 申请成功,则将内存放入内存池中,然后重新向内存池申请空间,然后结束 ;
代码
一级空间配置器
template < int inst>
class __malloc_alloc_template {
private :
static void * oom_malloc ( size_t) ;
public :
static void * allocate ( size_t n) {
void * result = malloc ( n) ;
if ( 0 == result)
result = oom_malloc ( n) ;
return result;
}
static void deallocate ( void * p, size_t )
{ free ( p) ; }
static void ( * set_malloc_handler ( void ( * f) ( ) ) ) ( ) {
void ( * old) ( ) = __malloc_alloc_oom_handler;
__malloc_alloc_oom_handler = f;
return ( old) ;
}
} ;
template < int inst>
void * __malloc_alloc_template < inst> :: oom_malloc ( size_t n) {
void ( * my_malloc_handler) ( ) ;
void * result;
for ( ; ; ) {
my_malloc_handler = __malloc_alloc_oom_handler;
if ( 0 == my_malloc_handler) {
__THROW_BAD_ALLOC;
}
( * my_malloc_handler) ( ) ;
result = malloc ( n) ;
if ( result)
return ( result) ;
}
}
typedef __malloc_alloc_template< 0 > malloc_alloc;
二级空间配置器
前期准备
template < int inst>
class __default_alloc_template {
private :
enum { __ALIGN = 8 } ;
enum { __MAX_BYTES = 128 } ;
enum { __NFREELISTS = __MAX_BYTES/ __ALIGN} ;
static size_t ROUND_UP ( size_t bytes) {
return ( ( ( bytes) + __ALIGN- 1 ) & ~ ( __ALIGN - 1 ) ) ;
}
private :
union obj{
union obj * free_list_link;
char client_data[ 1 ] ;
} ;
private :
static obj * free_list[ __NFREELISTS] ;
static size_t FREELIST_INDEX ( size_t bytes) {
return ( ( ( bytes) + __ALIGN- 1 ) / __ALIGN - 1 ) ;
}
static char * start_free;
static char * end_free;
static size_t heap_size;
} ;
申请空间
static void * allocate ( size_t n) {
obj * __VOLATILE * my_free_list;
obj * __RESTRICT result;
if ( n > ( size_t) __MAX_BYTES) {
return ( malloc_alloc:: allocate ( n) ) ;
}
my_free_list = free_list + FREELIST_INDEX ( n) ;
result = * my_free_list;
if ( result == 0 ) {
void * r = refill ( ROUND_UP ( n) ) ;
return r;
}
* my_free_list = result -> free_list_link;
return ( result) ;
}
填充内存块
template < int inst>
void * __default_alloc_template < inst> :: refill ( size_t n) {
int nobjs = 20 ;
char * chunk = chunk_alloc ( n, nobjs) ;
obj * * my_free_list;
obj * result;
obj * current_obj, * next_obj;
int i;
if ( 1 == nobjs)
return ( chunk) ;
my_free_list = free_list + FREELIST_INDEX ( n) ;
result = ( obj * ) chunk;
* my_free_list = next_obj = ( obj * ) ( chunk + n) ;
for ( i = 1 ; ; i++ ) {
current_obj = next_obj;
next_obj = ( obj * ) ( ( char * ) next_obj + n) ;
if ( nobjs - 1 == i) {
current_obj -> free_list_link = 0 ;
break ;
}
else {
current_obj -> free_list_link = next_obj;
}
}
return ( result) ;
}
向内存池中索要空间
template < int inst>
char * __default_alloc_template < inst> :: chunk_alloc ( size_t size, int & nobjs) {
char * result;
size_t total_bytes = size * nobjs;
size_t bytes_left = end_free - start_free;
if ( bytes_left >= total_bytes) {
result = start_free;
start_free += total_bytes;
return ( result) ;
}
else if ( bytes_left >= size) {
nobjs = bytes_left/ size;
total_bytes = size * nobjs;
result = start_free;
start_free += total_bytes;
return ( result) ;
}
else {
size_t bytes_to_get = 2 * total_bytes + ROUND_UP ( heap_size >> 4 ) ;
if ( bytes_left > 0 ) {
obj * * my_free_list = free_list + FREELIST_INDEX ( bytes_left) ;
( ( obj * ) start_free) -> free_list_link = * my_free_list;
* my_ree_list = ( obj * ) start_free;
}
start_free = ( char * ) malloc ( bytes_to_get) ;
if ( 0 == start_free) {
int i;
obj * * my_free_list, * p;
for ( i = size; i <= __MAX_BYTES; i += __ALIGN) {
my_free_list = free_list + FREELIST_INDEX ( i) ;
p = * my_free_list;
if ( 0 != p) {
* my_free_list = p -> free_list_link;
start_free = ( char * ) p;
end_free = start_free + i;
return ( chunk_alloc ( size, nobjs) ) ;
}
}
end_free = 0 ;
start_free = ( char * ) malloc_alloc:: allocate ( bytes_to_get) ;
}
heap_size += bytes_to_get;
end_free = start_free + bytes_to_get;
return ( chunk_alloc ( size, nobjs) ) ;
}
}
空间回收
static void deallocate ( void * p, size_t n) {
obj * q = ( obj * ) p;
obj * * my_free_list;
if ( n > ( size_t) __MAX_BYTES) {
malloc_alloc:: deallocate ( p, n) ;
return ;
}
my_free_list = free_list + FREELIST_INDEX ( n) ;
q -> free_list_link = * my_free_list;
* my_free_list = q;
}