在C语言中,我们设计接口往往会为对象设计两个接口:
- addref(void *)
- delref(void *)
使用场景比如:
obj *p = create_obj(); //引用计数为1
addref(p); //引用计数2
loop_queue(p); //放到队列中稍后处理。那么 需要先addref保证到时候使用到p的时候p没有被销毁。
p->xxx = yyy;
delref(p); //引用计数1
在cpp中,智能指针就起到类似作用。它帮助程序员更好地管理内存,不再malloc后忘记了free。
上面的例子就变成了:
MySharedPtr<obj> p(create_obj());
loop_queue(p); // 传参数会拷贝p生成一个临时变量,原始资源的引用计数会自动+1.
p->xxx = yyy;
智能指针协助你,没有了自己管理计数的麻烦。
关键几点:
- 默认构造函数
- 参数为指针的构造函数
- 拷贝构造函数
- 移动拷贝构造函数
- 析构函数
- get()
- use_count()
- 重载->
- 重载*
- swap
#include <iostream>
#include <stdio.h>
#include <assert.h>
using namespace std;
#define EXPECT_TRUE(exp) if (!(exp)) assert(0)
#define EXPECT_EQ(a, b) if ((a) != (b)) { \
cout << a << " != " << b << endl; \
assert(0); \
} else {\
printf("pass\n");\
}
template <typename T>
class MySharedPtr
{
public:
MySharedPtr() {
ref = NULL;
ptr = NULL;
}
// 析构
~MySharedPtr() {
if (!ref) {
return;
}
(*ref)--;
if (*ref == 0) {
delete ptr;
delete ref;
}
}
// 裸指针构造
MySharedPtr(T *raw) {
if (raw) {
ref = new int(1);
ptr = raw;
}
}
// 拷贝构造,共享资源,引用+1
MySharedPtr(const MySharedPtr &p) {
cout << "copy construct" << endl;
if (p.ptr) {
ptr = p.ptr;
ref = p.ref;
(*ref)++;
}
}
/**
* 移动构造,把p管理的资源偷到自己这里
* @param p 右值引用
*/
MySharedPtr(MySharedPtr &&p) {
cout << "move construct" << endl;
ref = NULL;
ptr = NULL;
this->swap(p);
}
// 操作符重载
void operator =(const MySharedPtr &p) {
// 交出自己的管理权
MySharedPtr<T> tmp;
this->swap(tmp);
ptr = p.ptr;
ref = p.ref;
(*ref)++;
}
T* operator ->() {
return ptr;
}
T& operator *() {
return *ptr;
}
int use_count() {
if (ref) {
return *ref;
}
return 0;
}
T *get() {
return ptr;
}
// 交换管理权
void swap(MySharedPtr &p) {
std::swap(this->ref, p.ref);
std::swap(this->ptr, p.ptr);
}
void reset() {
// 交出自己的管理权
MySharedPtr<T> tmp;
this->swap(tmp);
}
private:
int *ref;
T *ptr;
};
class Test
{
public:
Test() : n1(0), n2(0) {
cout << "Test created" << endl;
}
~Test() {
cout << "Test deleted" << endl;
}
Test(int arg) : n1(0), n2(0) {
cout << "Test created with arg:" << arg << endl;
n1 = arg;
}
int get_n1() {
return n1;
}
private:
int n1;
int n2;
};
void swap_test()
{
printf("------------------------%s--------------------------\n", __FUNCTION__);
MySharedPtr<Test> a(new Test(1024));
EXPECT_EQ(a.use_count(), 1);
MySharedPtr<Test> b;
b.swap(a);
EXPECT_EQ(a.use_count(), 0);
EXPECT_EQ(b.use_count(), 1);
}
void construct_test()
{
printf("------------------------%s--------------------------\n", __FUNCTION__);
MySharedPtr<Test> a(new Test(1024));
Test *p;
p = a.get();
EXPECT_EQ(p->get_n1(), 1024);
// 重载=
MySharedPtr<Test> b = a;
EXPECT_EQ(a.use_count(), 2);
// 拷贝构造
MySharedPtr<Test> c(b);
EXPECT_EQ(a.use_count(), 3);
// 重新赋值
MySharedPtr<Test> d(new Test);
c = d;
EXPECT_EQ(a.use_count(), 2);
}
void move_test()
{
printf("------------------------%s--------------------------\n", __FUNCTION__);
// 移动拷贝
MySharedPtr<Test> a(new Test(1024));
EXPECT_EQ(a.use_count(), 1);
MySharedPtr<Test> b(std::move(a));
EXPECT_EQ(a.use_count(), 0);
EXPECT_EQ(b.use_count(), 1);
EXPECT_EQ(b->get_n1(), 1024);
}
void reset_test()
{
// reset测试
printf("------------------------%s--------------------------\n", __FUNCTION__);
MySharedPtr<Test> a(new Test());
EXPECT_EQ(a.use_count(), 1);
a.reset();
EXPECT_EQ(a.use_count(), 0);
}
int main()
{
swap_test();
construct_test();
move_test();
reset_test();
return 0;
}
在C语言中,我们设计接口往往会为对象设计两个接口:
使用场景比如:
在cpp中,智能指针就起到类似作用。它帮助程序员更好地管理内存,不再malloc后忘记了free。
上面的例子就变成了:
智能指针协助你,没有了自己管理计数的麻烦。
关键几点: