前言
最近找实习,复习了一遍C++的知识,包括程序内存占用和C++11的一些特性等。故此记录。
c++程序内存占用
- 栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量,局部常量的值等。其操作方式类似于数据结构中的栈。
- 堆区(heap) — 一般由程序员分配释放(malloc/free, new/delete),若程序员不释放,程序结束后可能由操作系统回收。注意它与数据结构中的堆石两回事,分配方式倒是类似与链表。其中有malloc/free分配释放的也可以叫自由存储区。
- 全局区(静态存储区)(static)— 全局变量和静态变量,全局常量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放 (在C++中他们初始化和未初始化的共同占用一块内存区)。
- 文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
- 程序代码区—存放函数体的二进制代码。
C++函数栈空间的最大值 ,我的linux机器上面默认是8M,不过可以调整。
强制类型转换
static_cast
- 用于类层次结构中基类和子类之间指针或引用的转换。进行上行转换(把子类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成子类指针或引用)时,由于没有动态类型检查,所以是不安全的。
- 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
- 把void指针转换成目标类型的指针(不安全!!)
- 把任何类型的表达式转换成void类型。
- 不执行运行时类型检查
dynamic_cast
- 用于多态类型的转换
- 执行行运行时类型检查
- 只适用于指针或引用
- 要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换(即子类之间的转化),转化失败会返回空指针
- 运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表
creinterpret_cast
- 允许将任何指针转换为任何其他指针类型
- 也允许将任何整数类型转换为任何指针类型以及反向转换。
- 一个实际用途是在哈希函数中,即,通过让两个不同的值几乎不以相同的索引结尾的方式将值映射到索引。
const_cast
- 用于删除 const、volatile 和 __unaligned 特性(如将 const int 类型转换为 int 类型 )
RTTI
RTTI提供了以下两个非常有用的操作符:
(1)typeid操作符,返回指针和引用所指的实际类型。
(2)dynamic_cast操作符,将基类类型的指针或引用安全地转换为派生类型的指针或引用。
RTTI只适用于包含虚函数的类,用来判断基类指针指向的子类具体类型。对于存在虚函数的类型,typeid和dynamic_cast都会去查询type_info.
lambda
递归lambda。考虑一个实现Fibonacci函数的lambda。如果你试图用auto来声明,就会得到一个编译错误。
auto fib = [&fib](int n) {return n < 2 ? 1 : fib(n-1) + fib(n-2);};
error C3533: 'auto &': a parameter cannot have a type that contains 'auto' error C3531: 'fib': a symbol whose type contains 'auto' must have an initializer error C3536: 'fib': cannot be used before it is initialized error C2064: term does not evaluate to a function taking 1 arguments
问题出在auto意味着对象类型由初始表达式决定,然而初始表达式又包含了对其自身的引用,因此要求先知道它的类型,这就导致了无穷递归。解决问题的关键就是打破这种循环依赖,用std::function显式的指定函数类型:
std::function<int(int)> lfib = [&lfib](int n) {return n < 2 ? 1 : lfib(n-1) + lfib(n-2);};
其中,function<返回值类型(参数类型)>.
右值引用
左值与右值的根本区别在于是否允许取地址&运算符获得对应的内存地址。
右值引用的意义通常解释为两大作用:移动语义和完美转发。
右值引用是一种数据类型,既有右值性质,也有左值性质。右值引用变量绑定的对象C++11标准称之为“临终值”,临终值对象既有存储地址因此可以绑定到右值引用变量上,而且它又是一个即将停止使用的对象可以被移走内容。
移动语义:将临时变量内容转移到目标变量,不经历拷贝构造等等浪费空间的行为。
#include<utility> std::move//将左值强制类型转换为右值,该函数简单封装了static_cast<>
移动构造函数:
Someclass(Someclass && );
移动赋值运算符:
Someclass& operator=(Someclass &&);
重载
仅当const参数是一个引用或指针时,C++才允许基于const类型进行函数重载。
智能指针
- shared_ptr:引用计数策略,多个智能指针可以共享同一个对象,计数为0时销毁对象。
- unique_ptr:所有权策略,保证同一时间内只有一个智能指针可以指向该对象。可以移交所有权。
如果要将一个unique_ptr直接赋值给另一个,只有该unique_ptr为临时右值才可以,因为临时右值赋值以后很快被销毁,没有机会用它来访问无效的数据。
只有unique_ptr可以分配数组,即new [] 只有unique_ptr才能使用(unique_ptr\<T[],D>)
c语言实现c++封装继承和多态
在C语言中,可以用结构+函数指针来模拟类的实现,而用这种结构定义的变量就是对象。