1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| 一. 关于栈上的内存分配 1. 栈上的内存分配极为简单,移动一下栈指针即可 2. 栈上的释放内存也极为简单,函数执行结束时移动一下栈指针即可 3. 由于后进先出的执行过程,不可能出现内存碎片 对于有构造和析构函数的非简单类型,c++编译器会在生成代码的合适位置,插入对构造和析构函数的调用。编译器会自动调用析构函数,包括在函数执行发生异常的情况。
二. RAII c++支持将对象存储在栈上面,但某些情况对象不能存储在栈上。比如: 1. 对象很大 2. 对象的大小在编译时不能确定 3. 对象是函数的返回值,但由于特殊的原因,不应使用对象的值返回
不要返回本地变量的引用。在c++11之前,返回一个本地对象意味着这个对象会被拷贝,除非编译器发现可以做返回值优化(named return value optimization,或NRVO),能把对象直接构造到调用者的栈上。从c++11开始,返回值优化仍可以发生,但在没有返回值优化的情况下,编译器将试图把本地对象移动出去,而不是拷贝出去。这一行为不需要手工用 std::move() 干预,使用 std::move() 反而会影响返回值优化。
引用坍缩/引用折叠 在模版的使用中 template<typename T> void f(T&& param) f(10); // 10 是右值 int x = 10; f(x); // x 是左值
这种未定的引用类型称为 universal references,这种类型必须被初始化,而他是左值还是右值则取决于他的初始化,如果被左值初始化,那它就是左值,反之亦然。 由于存在 T&& 这种未定的引用类型,当它作为参数时,又可能被一个左值引用或右值引用的参数初始化,这是经过类型推导的 T&& 类型,相比右值引用(&&)会发生类型的变化,这种变化就称为引用折叠 1. 所有右值引用折叠到右值引用上仍然是一个右值引用 (A&&&& 变成 A&&) 2. 所有的其他引用类型之间的折叠都将变成左值引用 (A&& 变成 A&; A&&& 变成 A&) 简单一点,如下: 1. 对于 template<typename T> foo(T&&) 这样的代码,如果传递过去的参数时左值,T的推导结果是左值引用;如果传递过去的参数是右值,T的推导结果是参数的类型本身 2. 如果 T 是左值引用,那 T&& 的结果仍然是左值引用---type&&& 坍缩为 type& 3. 如果 T 是一个实际类型,那 T&& 的结果自然就是一个右值引用
完美转发 保持参数的值类型:左值仍然是左值,右值仍然是右值。不改变原值的属性 实现原理:实现了两个模版函数,一个接收左值,另一个接收右值
|