Scoped_Ptr(防拷贝)
上一篇博客中已经讲述了智能指针之AutoPtr,它存在很大的缺陷.所以有一种更粗暴的方法——Scoped_Ptr.
Auto_Ptr出现的问题都是由于进行拷贝构造和赋值运算符重载引起的资源不能得到有效的释放.那么就直接不要进行拷贝构造和赋值运算符重载,那么就不会引起那些问题了.
下面是简单的代码实现:
1 #pragma once
2
3 template<class T>
4 class Scoped_Ptr
5 {
6 public:
7 Scoped_Ptr(T* _ptr)
8 :ptr(_ptr)
9 {}
10 ~Scoped_Ptr()
11 {
12 if(ptr)
13 {
14 delete ptr;
15 }
16 }
17 T& operator*()
18 {
19 return *ptr;
20 }
21 T* operator->()
22 {
23 return ptr;
24 }
25 private:
26 Scoped_Ptr(Scoped_Ptr<T>& _ptr); //声明为私有,就不会有资源释放不当的问题产生.
27 Scoped_Ptr<T>& operator=(Scoped_Ptr<T>& _ptr);
28 protected:
29 T* ptr;
30 };
防拷贝的实现方法:
1.只声明不实现.
2.声明为私有.
SharePtr
就是使用引用计数的方法弥补智能指针的问题.
代码实现如下:
1 #pragma once
2 #include <stdio.h>
3
4 //声明,否则找不到
5 template<class T>
6 class Weak_Ptr;
7
8 template<class T>
9 class Share_Ptr
10 {
11 friend class Weak_Ptr<T>;
12 public:
13 Share_Ptr(T* _ptr)
14 :ptr(_ptr)
15 ,count(new int(1))
16 {}
17 ~Share_Ptr()
18 {
19 if(--(*count) == 0)
20 {
21 delete count;
22 if(ptr)
23 {
24 delete ptr;
25 printf("~Share_Ptr()\n"); //可以显示是否已经释放
26 }
27 }
28 }
29 T& operator*()
30 {
31 return *ptr;
32 }
33 T* operator->()
34 {
35 return ptr;
36 }
37 Share_Ptr(Share_Ptr<T>& s)
38 :ptr(s.ptr)
39 ,count(s.count)
40 {
41 ++(*count);
42 }
43 Share_Ptr<T>& operator=(Share_Ptr<T>& s)
44 {
45 //if(this != &s)
46 if(ptr != s.ptr)
47 {
48 if(--(*count) == 0)
49 {
50 delete ptr;
51 delete count;
52 }
53 ptr = s.ptr;
54 count = s.count;
55 ++(*count);
56 }
57 return *this;
58 }
59 protected:
60 T* ptr;
61 int* count;
62 };
63
64 template<class T>
65 class Weak_Ptr
66 {
67 public:
68 Weak_Ptr(const Share_Ptr<T>& s)
69 :ptr(s.ptr)
70 {}
71 T& operator*()
72 {
73 return *ptr;
74 }
75 T* operator->()
76 {
77 return ptr;
78 }
79 private:
80 T* ptr;
81 };
82
83 //struct ListNode
84 //{
85 // Share_Ptr<ListNode> prev;
86 // Share_Ptr<ListNode> next;
87 // int data;
88 //
89 // ListNode()
90 // :prev(NULL)
91 // ,next(NULL)
92 // {}
93 //};
94 struct ListNode
95 {
96 Weak_Ptr<ListNode> prev;
97 Weak_Ptr<ListNode> next;
98 int data;
99
100 ListNode()
101 :prev(NULL)
102 ,next(NULL)
103 ,data(int())
104 {}
105 };
106
107 void TestCycle()
108 {
109 //ListNode* n1 = new ListNode;
110 //ListNdoe* n2 = new ListNode;
111 //可能存在异常引起执行流乱跳不能正常释放资源
112 //delete n1;
113 //delete n2;
114
115 //使用智能指针,但是存在循环引用的问题
116 Share_Ptr<ListNode> n1(new ListNode);
117 Share_Ptr<ListNode> n2(new ListNode);
118 n1->next = n2;
119 n2->prev = n1;
120
121 //解决智能指针的循环引用的问题,增加一个weak_ptr弱指针去辅助智能指针,
122 //只要在循环引用中不增加引用计数就可以避免这样的问题.
123
124 }
存在问题如下图所示:
存在问题:循环引用. 解决:引入Weak_Ptr即让其引用计数不加加就好. |
总结:
Auto_Ptr:存在问题:可能会引起资源不能及时释放.
解决1:管理权转移法.(仍然存在问题,所以不建议使用)
解决2:Scoped_Ptr防拷贝智能指针,防止拷贝构造和赋值运算符的重载.
解决3:Share_Ptr引用计数法,需要配合Weak_Ptr的使用,防止产生循环引用的问题.
问题2:由于new的可能是一个单独的对象,也可能是一组对象.解决这个问题可以有两种方法:
解决1:再写一个类去实现数组的delete[];
解决2:通过仿函数来直接通过对象来模拟函数去正确的释放资源.
仿函数
通过重载(),就可以用对象名来调用(),像函数一样实现某些功能.
例如:
1 #include <iostream>
2
3 using namespace std;
4
5 struct Compare
6 {
7 bool operator()(int x,int y)
8 {
9 return x>y;
10 }
11 };
12 int main()
13 {
14 Compare compare;
15 cout<<compare(3,2)<<endl;
16 return 0;
17 }
仿函数呢,还可以在调用标准库的SORT算法时进行传参,因为排序可以从小到大也可以从大到小,在传参时第二个参数就可以通过仿函数来实现.
对于上面的Share_Ptr智能指针,由于如果是new了数组,我们也可以通过仿函数来得到有效的释放资源.具体做法如下:
1 #include <iostream>
2
3 using namespace std;
4
5 template<class T>
6 struct deleteArr
7 {
8 void operator()(T* ptr)
9 {
10 delete[] ptr;
11 }
12 };
^~^