问题
C++中最令人头疼的问题是强迫程序员对申请的资源(文件、内存等)进行管理,一不小心就会出现内存泄漏问题(忘记对申请的资源释放)
注解:
- 因为有这一句:int *p = new int; 当调试运行出去main()函数作用域的时候(1处),堆内存依然存在(2处).
#include <iostream> using namespace std; int main() { /* 创建一个指针,这样在运行的时候编译器会在堆上面 分配内存,new会在堆上分配4个字节的空间 C++需要程序员自己管理堆内存的申请和释放。 对于申请,即是使用时申请。 但是对于释放,要不要释放?何时释放?释放之后会不会 引发一些问题?这些都需要考虑。 */ int *p = new int; //cout << "hello,world!" << endl; //system("pause"); return 0; }
注解:
- C++中不仅仅是申请内存需要去管理,申请打开一个文件也需要去管理的。
- 在使用完之后需要释放的或者归还的东西,都叫做资源。
- 使用资源不归还,会造成资源浪费(如内存泄漏)。比如一个循环里面有申请内存语句,但是忘记写释放内存语句了,程序运行后,内存会越来越小。
FILE* open_file(char* name) { FILE* fp = NULL; fp = fopen(name, "rb+"); return fp; }
- java和python语言使用了垃圾回收机制,编译器会自己处理,不在需要认为管理,相关虚拟机会自动释放不需要使用的资源。C++没有使用垃圾回收机制。
- C++的解决办法:RAII
在传统C++里面我们只好使用new和delete去【记得】对资源进行释放。而C++11引入了智能指针的概念,在语法层面对动态内存进行释放。它使用了引用计数的想法,让程序员不再需要关心手动释放内存。
解决思路:利用C++中一个对象出了其作用域会被自动析构,因此我们只需要考虑在析构函数的时候申请空间,而在析构函数(在离开作用域时调用)的时候释放空间,这样,就减轻了程序员在编码过程中考虑资源释放的为题,这就是RAII。
RAII:Resource Acquisition Is Initialization, 是C++所特有的资源管理方式。
class CTest { public: CTest() { std::cout << "CTest()" << std::endl; //在构造中完成资源的初始化 m_pInt = new int; } ~CTest() { std::cout << "~CTest()" << std::endl; if (m_pInt != nullptr) { //在析构中完成资源的释放 delete m_pInt; } } private: int* m_pInt; }; CTest test; //这是一个全局对象
这样,就不用管理资源了,实际上是利用了离开对象作用域的时候析构函数会被自动调用这一特点。后面所有的核心都是利用类的构造和析构会被自动调用这一特点进行的。
具体而言,C11的stl中为大家带来了3种只能指针,正确合理的使用可以有效的帮助程序员管理资源,当然,在C++的使用智能指针没有像java,python这种具备垃圾回收机制的语言那么舒适,毕竟,程序员还需要做一些额外的事情,但是,这远比传统的C或者C++更加优雅。
3种智能指针分别是:
- std::shared_ptr 强指针
- std::unique_ptr
- std::wear_ptr 弱指针
早起有一个auto_ptr,这4种指针在使用上有区别:
auto_ptr有缺陷是过时的产物。
std::unique_ptr对auto_ptr的问题进行了修正。
std::shared_ptr使用了引用计数,但是会出现循环引用的问题需要配合后面的std::wear_ptr 一起使用。