参考自《C5-C++ Primer》和实验楼相关实验。
一. 智能指针概念
- 智能指针的引入:
C++中,通过new/delete这对运算符进行动态内存的管理。动态内存使用很容易出问题,因为确保在正确时间释放内存是很困难的。有时忘记释放内存,产生内存泄漏;有时在指针还引用内存时就释放了它,产生引用非法内存的指针。因此,为了更容易和安全地使用动态内存,C++11提供了智能指针(smart pointer)类型来管理动态对象。
- 智能指针的使用:
标准提供两种智能指针:shared_ptr允许多个指针指向同一个对象;unique_ptr独占所指对象;weak_ptr指向shared_ptr所管理的对象。本实验主要实现shared_ptr的功能。
- 智能指针的实现原理:
1. 析构函数,对象被销毁时会被调用的一个函数,对于基于栈的对象而言,如果对象离开其作用域则对象会被自动销毁,而此时析构函数也自动会被调用。
2. 引用计数技术,维护一个计数器用于追踪资源(如内存)的被引用数,当资源被引用时,计数器值加1,当资源被解引用时,计算器值减1。
智能指针的大致实现原理就是在析构函数中,检查所引用对象的引用计数,如果引用计数为0,则真正释放该对象内存。
二. 实现版本v1
- 定义智能指针类
- 创建构造函数:具备默认构造函数;指定类型构造函数
- 定义为模板类
- 析构函数:释放内存
/* * file name : smartpointer.h * desp : 智能指针版本v1 */ #ifndef __SMARTPOINTER_H__ #define __SMARTPOINTER_H__ template <typename T> // 将智能指针类定义成模板类 class SmartPointer { public: // 默认构造函数 SmartPointer():mPointer(NULL) {std::cout <<"create unknown smart pointer."<< std::endl;} // 接收不同指针类型的构造函数 SmartPointer(T *p):mPointer(p) {std::cout <<"create smart pointer at "<<static_cast<const void*>(p)<<std::endl;} // 析构函数 ~SmartPointer(){ std::cout << "release smart pointer at "<<static_cast<const void*>(mPointer)<<std::endl; // 实现内存资源自动销毁机制 if (mPointer) delete mPointer; } private: T *mPointer; // 指向智能指针实际对应的内存资源,根据参数自动推导规则,定义内部资源指针类型 }; #endif // __SMARTPOINTER_H__
测试智能指针:测试文件 sptestcase1.cpp
/* * file name : sptestcase1.cpp * desp : 智能指针测试代码 case1 测试智能指针的创建与销毁 */ #include <iostream> #include "smartpointer.h" class SomeClass{ public: SomeClass(){std::cout << "SomeClass default constructor !"<<std::endl;} }; void testcase1() { // 创建一个不知所指的智能指针 SmartPointer<char> spunknown; // 创建空智能指针 SmartPointer<char> spnull = NULL; // 创建指向具体类的智能指针 SmartPointer<SomeClass> spclass = new SomeClass; // 创建字符串的智能指针 SmartPointer<const char> spstr = "Hello world!"; } int main(void) { testcase1(); return 0; }
编译执行:
$ g++ -o sptestcase1 sptestcase1.cpp $ ./sptestcase1
运行结果分析:
- V1版本不足
简单实现智能指针类,智能指针释放指向“hello world”指针时出错。我们的智能指针不能指向无法被delete释放的内存资源。
三. 知识点查漏补缺
- new的不同用法
(1)int *p=new int;//无初始化 (2)int *p=new int(5);//初始化为5 (3)创建类 Test *test=new Test(); delete test;//加括号调用没有参数的构造函数,不加括号调用默认构造函数或唯一的构造函数 (4)int *p=new int[10];//开辟容量为10的数组 delete []p;
- 类模板
//类的定义 template<class T> class Test { private: T n; const T i; public: Test():i(0) {} Test(T k); ~Test(){} void print(); T operator+(T x); }; //类的使用 类名<实际的类型> eg.Test<int> test;//声明一个对象
- staic_cast<const void *>(p)
//地址?转换类型?
我们这里定义的是模版类,因为不知道这个指针的具体类型。使用使用了const void *。
const void * p 这是定义了一个指针p,p可以指向任意类型的值,但它指向的值必须是常量。在这种情况下,我们不能修改被指向的对象,但可以使指针指向其他对象。