一种解决指针无效引用的方法

无效引用

  • 当多个指针指向同一个对象,在其中一个指针被delete后
  • 其他指向该对象的指针不知道,其指向的内存已经无效
  • 当访问指针指向的数据时出现访问错误。

项目优化的需求

  • 避免项目中无效引用的使用
  • 在尽量少的变更代码的条件下能够实现该优化
  • 不影响正常的逻辑

解决方案

我们通过对被管理对象加标签,以及使用弱指针来代替旧的指针的方式来实现。

结构图

标签

可以把标签理解成一个书签。在对象创建时,对象中带有一个没有写任何内容的空白书签,在对象指针赋值给弱指针时,会在书签中填充内容,内容为对象的指针,并在弱指针中保存书签指针。

被管理对象通过继承标签指针来具备书签功能。

弱指针

在说弱指针之前,先描述下我们定义的强指针,只要强指针存在,其指向的内容一定存在。

这里的弱指针相对于强指针弱指针存在,其指向的内容可以不存在。既在弱指针指向的内存被释放,我们能够及时知道当前指针已经无效。

把对指针的所有操作封装到弱指针类中,让其能够模拟指针,这样可以在旧代码不变动的条件下,也就是说使用者可以完全不知道弱指针的存在,任然像正常指针一样使用。

利用弱指针的这种特性,我们正好能够解决无效引用带来的问题,并且能够满足优化提到的三点需求。


代码
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
#ifndef _WEAK_TAG_POINTER_H_
#define _WEAK_TAG_POINTER_H_

#include <stdio.h>
#include <assert.h>

template<typename T> class WeakPointer;

template<typename T>
class WeakTagPointer {
private:
/**
* @desc 标签 1. 被标识类继承标签指针
* 2. 当被标识类构建对象时,标签指针中引用计数默认为1
* 3. 只有当被标识对象释放,标签中的pinter指针会被置为NULL
* 4. 当标签的引用计数为0时,会释放标签自身
*/

struct WeakTag {
WeakTag(T* p): pointer(p), count(1) { }

/**
* @desc 减少引用计数,没有被引用释放自身
*/

void WeakRelease()
{

--count;
if (count <= 0) {
delete this;
}
}

/**
* @desc 增加引用计数
*/

void WeakAddRef() { ++count; }

/**
* @desc 获取标签指针
*/

T* Pointer() const { return pointer; }

/**
* @desc 设置标签指针
*/

void SetPointer(T* pObj) { pointer = pObj; }

private:
T* pointer; //标签指针
int count; //引用计数
};

public:
WeakTagPointer() : m_pWeakTag(NULL) {}

/**
* @desc 当子类(及被标识对象)析构时, 触发父类析构。
* 1. 被标识对象释放,pointer标识被置NULL
* 2. 通知其他使用者,该指针已无效
*/

virtual ~WeakTagPointer()
{
if (m_pWeakTag != NULL) {
m_pWeakTag->SetPointer(NULL);
m_pWeakTag->WeakRelease();
}
}

public:
/**
* @desc 获取标签指针
* 1. 指针未空时创建并返回, this指针为被标识对象
* 2. 每次调用该函数,引用计数加1
*/

WeakTag* TagPointer()
{

if (m_pWeakTag == NULL) {
m_pWeakTag = new WeakTag(static_cast<T*>(this));
}

if (m_pWeakTag != NULL) {
m_pWeakTag->WeakAddRef();
}
return m_pWeakTag;
}

private:
template<typename T2> friend class WeakPointer;
WeakTag* m_pWeakTag;
};

//////////////////////////////////////////////////////////////////////////

/**
* WeakPointer类,模拟指针的各种操作
*/

template<typename T>
class WeakPointer {
//typedef typename T::WeakTag WeakTagType;
struct WeakTagType : public T::WeakTag {};

public:
WeakPointer():m_pTag(NULL) {}
~WeakPointer()
{
Release();
}

public:
/**
* @desc 获取标签指针
*/

T* operator->()
{
return GetPointer();
}

/**
* @desc 获取指针对应的引用数据
*/

T& operator*()
{
T* p = GetPointer();
assert(p != NULL);
return *p;
}

/**
* @desc bool重载
*/

operator bool()
{

return IsValid();
}

//////////////////////////////////////////////////////////////////////////
//等于
//////////////////////////////////////////////////////////////////////////
/**
* @desc 对指定类型指针赋值
*/

void operator=(T* pObj)
{
WeakTagType* pTag = NULL;
if (pObj != NULL) {
//获取对象中标识
WeakTagType* pTag = static_cast<WeakTagType*>(pObj->TagPointer());
if (pTag != NULL) {
//释放自身标识数据
Release();
}
}
//赋值
m_pTag = pTag;
}

/**
* @desc 同类型pointer的赋值
* @param [in] pObj 同类型弱指针
*/

WeakPointer& operator=(WeakPointer& obj)
{
if (this != &obj) {
operator=(obj.GetPointer());
}
return *this;
}

/**
* @desc 不同类型pointer的赋值
* @param [in] pObj 其他类型弱指针
*/

template<typename T2>
void operator=(WeakPointer<T2>& obj)
{
operator=(obj.GetPointer());
}

//////////////////////////////////////////////////////////////////////////
//等于等于
//////////////////////////////////////////////////////////////////////////

/**
* @desc 判断同类型弱指针是否相同
*/

bool operator==(WeakPointer& obj)
{
return IsEqual(obj);
}

/**
* @desc 判断和表示类型的指针是否相同
*/

bool operator==(T* pObj)
{
return IsEqual(pObj);
}

/**
* @desc 判断和表示类型的指针是否相同, 特例NULL
*/

bool operator==(const int&)
{
return !IsValid();
}

/**
* @desc 判断和其他类型的弱指针是否不同
*/

template<typename T2>
bool operator==(WeakPointer<T2>& obj)
{
return IsEqual(obj);
}

//////////////////////////////////////////////////////////////////////////
//不等于
//////////////////////////////////////////////////////////////////////////
/**
* @desc 判断同类型弱指针是否不同
*/

bool operator!=(WeakPointer& obj)
{
return !IsEqual(obj);
}

/**
* @desc 判断和指针是否不同, 特例NULL
*/

bool operator!=(const int&)
{
return IsValid();
}

/**
* @desc 判断和表示类型的指针是否不同
*/

bool operator!=(T* pObj)
{
return !IsEqual(pObj);
}

//////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////
/**
* @desc 获取标签指针
*/

T* GetPointer()
{

T* p = NULL;
if (m_pTag != NULL) {
p = static_cast<T*>(m_pTag->Pointer());
}
return p;
}

/**
* @desc 判断标签的有效性
* @return 是否有效
*/

bool IsValid() const
{

return (m_pTag != NULL) && (m_pTag->Pointer() != NULL);
}

private:
WeakPointer(const WeakPointer&);

/**
* @desc 判断两个同类型弱指针是否相同
*/

bool IsEqual(WeakPointer& obj)
{

return IsEqual(obj.m_pTag, obj.GetPointer());
}

/**
* @desc 判断两个弱指针是否相同
*/

template<typename T2>
bool IsEqual(WeakPointer<T2>& obj)
{

return false;
}

bool IsEqual(WeakTagType* pTag, T* pObj)
{

bool b = false;
if (m_pTag == NULL && pTag == NULL) {
b = true;
} else if (m_pTag != NULL && pTag != NULL) {
bool b0 = (m_pTag == pTag);
bool b1 = (m_pTag->Pointer() == pObj);
b = (b0 && b1);
}
return b;
}

//
bool IsEqual(T* pObj)
{

bool b = false;
if (m_pTag != NULL && pObj != NULL) {
b = (m_pTag->Pointer() == pObj);
}
return b;
}

/**
* 释放标签数据
*/

void Release()
{

if (m_pTag != NULL) {
m_pTag->WeakRelease();
}
}

private:
template<typename T2> friend class WeakPointer;
WeakTagType* m_pTag;
};

#endif //_WEAK_TAG_POINTER_H_

完整测试代码