【原创】借助Functor用C++实现类似C#的Event对象

好久没用C++了,今天整理以前写的代码,发现一个以前用C++写的Event模板类,

虽然写出来之后都没怎么用过,不过个人感觉应该蛮好用的,这里贴出来,大家帮忙找找碴。

1,使用方法

 先看看这个Event类怎么使用的吧:

01
02
03
04
05
06
07
08
09
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

#include <stdio.h>
#include <vector>
#include "Event.h" // 引用头文件

typedef Event<TYPE_LIST2(int, int)> EventIntIntType; // typedef 一下模板类型, 当然你也可以不定义

// 指挥官
class Commander
{
public:
  EventIntIntType TargetChangedEvent; // 定义事件

  void ChangeTarget(int x, int y)
  {
    // 所有听到这个广播的士兵,向目标进发
    TargetChangedEvent(x, y); // 广播事件
  }
};

// 士兵
class Soldier
{
public:
  Soldier(int id, Commander* commander)
   : _id(id)
   , _commander(commander)
  {
    // 监听指挥官的广播 
     _commander->TargetChangedEvent.Add(EventIntIntType::Handler(this, &Soldier::ChangeTarget)); // 注册事件

    // 这里也可以这样使用
    // Functor<void, TYPE_LIST2(int, int)> functor(this, &Soldier::ChangeTarget);
    // _commander->TargetChangedEvent.Add(functor);
  }

  ~Soldier()
  {
    printf("Soldier No%d is dead!\n\n", _id);
    // 我牺牲了,指挥官不要给我发广播了
    _commander->TargetChangedEvent.Remove(EventIntIntType::Handler(this, &Soldier::ChangeTarget)); // 注消事件
  }

  void ChangeTarget(int x, int y)
  {
    printf("Soldier No%d changed target to location(%d, %d)\n", _id, x, y);
  }

private:
  int _id;
  Commander* _commander;
};

int _tmain(int argc, _TCHAR* argv[])
{
  Commander commander;
  Soldier *soldier1, *soldier2, *soldier3;
  soldier1 = new Soldier(111, &commander);
  soldier2 = new Soldier(222, &commander);
  soldier3 = new Soldier(333, &commander);
  commander.ChangeTarget(100, 100);

  delete soldier2;

  commander.ChangeTarget(200, 200);

  return 0;
}

 

简单说明一下示例程序,程序里有两个类,指挥官Commander类和士兵Soldier类

Commander类中定义了一个事件对象:

EventIntIntType TargetChangedEvent; // 定义事件
 这个EventIntIntType是在前面typedef的模板类型定义:

typedef Event<TYPE_LIST2(int, int)> EventIntIntType;

这个TYPE_LIST2是一个宏定义,就是类型列表,

因为这里定义的是两个int参数的事件,所以使用TYPE_LIST2

当然也有,

TYPE_LIST1

TYPE_LIST2

……

最多到

TYPE_LIST6

(我想应该够用了吧?)

对了,如果这个Event不需要参数,那么使用Event<void>。

(这个<void>有没有可能去掉?!)

然后Soldier类的构造函数中,把自己的

void ChangeTarget(int x, int y)函数注册到Commander的TargetChangedEvent事件中;

    // 监听指挥官的广播
    _commander->TargetChangedEvent.Add(EventIntIntType::Handler(this, &Soldier::ChangeTarget)); // 注册事件

这里其实就是新建了一个Functor<int,int>,加到Event<int,int>的链表中,

为了方便使用,我这里是在Event中加了一个Handler类型,

当然你也可以直接新建一个这种类型的Functor加进去,

    // 这里也可以这样使用
    // Functor<void, TYPE_LIST2(int, int)> functor(this, &Soldier::ChangeTarget);
    // _commander->TargetChangedEvent.Add(functor);

另外要记住,在Soldier类的析构函数中,把自己注册的Event注销掉,

_commander->TargetChangedEvent.Remove(EventIntIntType::Handler(this, &Soldier::ChangeTarget)); // 注消事件
看到这里,你可能会有个疑问:为什么这里还需要新建一个Functor传进去?

因为注销的时候,其实是比较Functor是否相等,如果Functor相等,那么才会注销,

Functor比较是比较的对象指针和成员函数指针,只要两者相等,就表示Functor是相等的。

所以这里还是需要再传个Functor进去比较的。

 

在Commander中,如果需要广播事件,只要这样调用就可以了:

    // 所有听到这个广播的士兵,向目标进发
    TargetChangedEvent(x, y); // 广播事件

怎么样?用法很简单吧?!

上面代码是可以直接编译运行的,只要把下面的Functor.h和Event.h文件加到工程里就可以了。

 

2,实现

下面看看实现吧。

 

对于C#的Event的对象,我的研究的不深,只是知道怎么使用。

从我使用的角度看,我的理解C#中的Event就是一个delegate的集合对象,

在特定的时候调用所有delegate,完成事件的广播动作。

 

那什么是delegate?

我的理解就是对象化的函数指针。

 

 那按照这个思路考虑的话,要用C++实现Event,

首先就要实现出类似delegate的东西,这个C++类似的就是Functor了,

对于Functor应该都不陌生了,我是参照这篇博文的设计实现的:

http://www.cppblog.com/kevinlynx/archive/2008/03/17/44678.html

 有了Functor,Event不过就是一个Functor的集合的包装而已。

 具体就不一一说明了,直接上代码,代码才是硬道理。

 

 Functor.h和Event.h的代码如下:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
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
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
//=Functor.h=======================================================================
// Created : 2009-10-08
// Description : template Functor class
// Usage :
#if 0
typedef Functor<void, void> FunctorVoid;
typedef Functor<int, TYPE_LIST3(int, bool)> FunctorIntBool;
FunctorVoid fnt1(&Print);
fnt1();
FunctorIntBool fnt2(&func1);
fnt2(12, true);
FunctorIntBool fnt3(&a1, &A1::func);
fnt3(12, true);
#endif
//
//=================================================================================

#pragma once

struct null_type { };
template <typename _T, typename _U>
struct type_list
{
  typedef _T head_type;
  typedef _U tail_type;
};
#define TYPE_LIST1( T1 ) type_list<T1, null_type>
#define TYPE_LIST2( T1, T2 ) type_list<T1, TYPE_LIST1( T2 ) >
#define TYPE_LIST3( T1, T2, T3 ) type_list<T1, TYPE_LIST2( T2, T3 ) >
#define TYPE_LIST4( T1, T2, T3, T4 ) type_list<T1, TYPE_LIST3( T2, T3, T4 ) >
#define TYPE_LIST5( T1, T2, T3, T4, T5 ) type_list<T1, TYPE_LIST4( T2, T3, T4, T5 ) >
#define TYPE_LIST6( T1, T2, T3, T4, T5, T6 ) type_list<T1, TYPE_LIST5( T2, T3, T4, T5, T6 ) >

template <typename _R>
struct handler_type_base
{
  typedef _R result_type;
  typedef null_type param1_type;
  typedef null_type param2_type;
  typedef null_type param3_type;
  typedef null_type param4_type;
  typedef null_type param5_type;
  typedef null_type param6_type;
};

//
// handler_base
//
template <typename _R, typename _ParamList>
struct handler_base;

// 0 param
template <typename _R>
struct handler_base<_R, void> : public handler_type_base<_R>
{
  virtual _R operator() ( void ) = 0;
  virtual bool is_equal(handler_base*) = 0;
  virtual handler_base* clone() = 0;
};

// 1 param
template <typename _R, typename _P1>
struct handler_base<_R, TYPE_LIST1( _P1 ) > : public handler_type_base<_R>
{
  typedef _P1 param1_type;

  virtual _R operator() ( _P1 ) = 0;
  virtual bool is_equal(handler_base*) = 0;
  virtual handler_base* clone() = 0;
};

// 2 param
template <typename _R, typename _P1, typename _P2>
struct handler_base<_R, TYPE_LIST2( _P1, _P2 ) > : public handler_type_base<_R>
{
  typedef _P1 param1_type;
  typedef _P2 param2_type;

  virtual _R operator() ( _P1, _P2 ) = 0;
  virtual bool is_equal(handler_base*) = 0;
  virtual handler_base* clone() = 0;
};

// 3 param
template <typename _R, typename _P1, typename _P2, typename _P3>
struct handler_base<_R, TYPE_LIST3( _P1, _P2, _P3 ) > : public handler_type_base<_R>
{
  typedef _P1 param1_type;
  typedef _P2 param2_type;
  typedef _P3 param3_type;

  virtual _R operator() ( _P1, _P2, _P3 ) = 0;
  virtual bool is_equal(handler_base*) = 0;
  virtual handler_base* clone() = 0;
};

// 4 param
template <typename _R, typename _P1, typename _P2, typename _P3, typename _P4>
struct handler_base<_R, TYPE_LIST4( _P1, _P2, _P3, _P4 ) > : public handler_type_base<_R>
{
  typedef _P1 param1_type;
  typedef _P2 param2_type;
  typedef _P3 param3_type;
  typedef _P4 param4_type;

  virtual _R operator() ( _P1, _P2, _P3, _P4 ) = 0;
  virtual bool is_equal(handler_base*) = 0;
  virtual handler_base* clone() = 0;
};

// 5 param
template <typename _R, typename _P1, typename _P2, typename _P3, typename _P4, typename _P5>
struct handler_base<_R, TYPE_LIST5( _P1, _P2, _P3, _P4, _P5 ) > : public handler_type_base<_R>
{
  typedef _P1 param1_type;
  typedef _P2 param2_type;
  typedef _P3 param3_type;
  typedef _P4 param4_type;
  typedef _P5 param5_type;

  virtual _R operator() ( _P1, _P2, _P3, _P4, _P5 ) = 0;
  virtual bool is_equal(handler_base*) = 0;
  virtual handler_base* clone() = 0;
};

// 6 param
template <typename _R, typename _P1, typename _P2, typename _P3, typename _P4, typename _P5, typename _P6>
struct handler_base<_R, TYPE_LIST6( _P1, _P2, _P3, _P4, _P5, _P6 ) > : public handler_type_base<_R>
{
  typedef _P1 param1_type;
  typedef _P2 param2_type;
  typedef _P3 param3_type;
  typedef _P4 param4_type;
  typedef _P5 param5_type;
  typedef _P6 param6_type;

  virtual _R operator() ( _P1, _P2, _P3, _P4, _P5, _P6 ) = 0;
  virtual bool is_equal(handler_base*) = 0;
  virtual handler_base* clone() = 0;
};

//
// class handler
//
template <typename _R, typename _ParamList, typename _FuncType>
class handler : public handler_base<_R, _ParamList>
{
public:
  typedef _FuncType func_type;

  typedef handler_base<_R, _ParamList> base_type;
  typedef typename base_type::param1_type param1_type;
  typedef typename base_type::param2_type param2_type;
  typedef typename base_type::param3_type param3_type;
  typedef typename base_type::param4_type param4_type;
  typedef typename base_type::param5_type param5_type;
  typedef typename base_type::param6_type param6_type;

public:
  handler( const func_type &func ) :
    _func( func )
  {
  }

  _R operator() ()
  {
    return _func();
  }

  _R operator() ( param1_type p )
  {
    return _func( p );
  }

  _R operator() ( param1_type p1, param2_type p2 )
  {
    return _func( p1, p2 );
  }

  _R operator() ( param1_type p1, param2_type p2, param3_type p3 )
  {
    return _func( p1, p2, p3 );
  }

  _R operator() ( param1_type p1, param2_type p2, param3_type p3, param4_type p4 )
  {
    return _func( p1, p2, p3, p4 );
  }

  _R operator() ( param1_type p1, param2_type p2, param3_type p3, param4_type p4, param5_type p5 )
  {
    return _func( p1, p2, p3, p4, p5 );
  }

  _R operator() ( param1_type p1, param2_type p2, param3_type p3, param4_type p4, param5_type p5, param6_type p6 )
  {
    return _func( p1, p2, p3, p4, p5, p6 );
  }

  bool is_equal(base_type* r_value)
  {
    handler* p = (handler*)(r_value);
    if (_func != p->_func)
    {
      return false;
    }
    return true;
  }

  base_type* clone()
  {
    base_type* p = new handler(_func);
    return p;
  }

private:
  func_type _func;
};

//
// class member_handler
//
template <typename _R, typename _ParamList, typename _FuncType, typename _ObjType>
class member_handler : public handler_base<_R, _ParamList>
{
public:
  typedef _FuncType func_type;
  typedef _ObjType obj_type;

  typedef handler_base<_R, _ParamList> base_type;
  typedef typename base_type::param1_type param1_type;
  typedef typename base_type::param2_type param2_type;
  typedef typename base_type::param3_type param3_type;
  typedef typename base_type::param4_type param4_type;
  typedef typename base_type::param5_type param5_type;
  typedef typename base_type::param6_type param6_type;

public:
  member_handler( obj_type *obj, const func_type &func ) :
    _obj( obj ), _func( func )
  {
  }

  _R operator() ()
  {
    return (_obj->*_func)();
  }

  _R operator() ( param1_type p )
  {
    return (_obj->*_func)( p );
  }

  _R operator() ( param1_type p1, param2_type p2 )
  {
    return (_obj->*_func)( p1, p2 );
  }

  _R operator() ( param1_type p1, param2_type p2, param3_type p3 )
  {
    return (_obj->*_func)( p1, p2, p3 );
  }

  _R operator() ( param1_type p1, param2_type p2, param3_type p3, param4_type p4 )
  {
    return (_obj->*_func)( p1, p2, p3, p4 );
  }

  _R operator() ( param1_type p1, param2_type p2, param3_type p3, param4_type p4, param5_type p5 )
  {
    return (_obj->*_func)( p1, p2, p3, p4, p5 );
  }

  _R operator() ( param1_type p1, param2_type p2, param3_type p3, param4_type p4, param5_type p5, param6_type p6 )
  {
    return (_obj->*_func)( p1, p2, p3, p4, p5, p6 );
  }

  bool is_equal(base_type* r_value)
  {
    member_handler* p = (member_handler*)(r_value);
    if (_obj != p->_obj)
    {
      return false;
    }
    if (_func != p->_func)
    {
      return false;
    }
    return true;
  }

  base_type* clone()
  {
    base_type* p = new member_handler(_obj, _func);
    return p;
  }

private:
  obj_type *_obj;
  func_type _func;
};

//
// class Functor
//
template <typename _R, typename _ParamList>
class Functor
{
public:
  typedef handler_base<_R, _ParamList> handler_type ;

  typedef typename handler_type::param1_type param1_type;
  typedef typename handler_type::param2_type param2_type;
  typedef typename handler_type::param3_type param3_type;
  typedef typename handler_type::param4_type param4_type;
  typedef typename handler_type::param5_type param5_type;
  typedef typename handler_type::param6_type param6_type;

public:
  template <typename _FuncType>
  Functor( _FuncType func ) :
    _handler( new handler<_R, _ParamList, _FuncType>( func ) )
  {
  }

  template <typename _ObjType, typename _FuncType>
  Functor( _ObjType *obj, _FuncType func ) :
    _handler( new member_handler<_R, _ParamList, _FuncType, _ObjType>( obj, func ) )
  {
  }

  Functor( const Functor& f )
  {
    _handler = f._handler->clone();
  }

  ~Functor()
  {
    delete _handler;
  }

  _R operator() ()
  {
    return (*_handler)();
  }

  _R operator() ( param1_type p )
  {
    return (*_handler)( p );
  }

  _R operator() ( param1_type p1, param2_type p2 )
  {
    return (*_handler)( p1, p2 );
  }

  _R operator() ( param1_type p1, param2_type p2, param3_type p3 )
  {
    return (*_handler)( p1, p2, p3 );
  }

  _R operator() ( param1_type p1, param2_type p2, param3_type p3, param4_type p4 )
  {
    return (*_handler)( p1, p2, p3, p4 );
  }

  _R operator() ( param1_type p1, param2_type p2, param3_type p3, param4_type p4, param5_type p5 )
  {
    return (*_handler)( p1, p2, p3, p4, p5 );
  }

  _R operator() ( param1_type p1, param2_type p2, param3_type p3, param4_type p4, param5_type p5, param6_type p6 )
  {
    return (*_handler)( p1, p2, p3, p4, p5, p6 );
  }

  bool operator==(const Functor& r_value)
  {
    return _handler->is_equal(r_value._handler);
  }

  bool operator!=(const Functor& r_value)
  {
    return !_handler->is_equal(r_value._handler);
  }

  Functor& operator=(const Functor& r_value)
  {
    delete _handler;
    _handler = r_value._handler->clone();
    return *this;
  }

private:
  handler_type *_handler;
};

 

 

 

 

 下面是Event类的实现文件:Event.h

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
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

// Event.h

#pragma once
#include <list>
#include <algorithm>
#include "Functor.h"

template <typename _ParamList>
class Event
{
public:
  Event()
    : _recursive(false)
    , _calling(false)
    , _enabled(true)
  {
  }

  typedef Functor<void, _ParamList> Handler;

  typedef handler_base<void, _ParamList> handler_type ;

  typedef typename handler_type::param1_type param1_type;
  typedef typename handler_type::param2_type param2_type;
  typedef typename handler_type::param3_type param3_type;
  typedef typename handler_type::param4_type param4_type;
  typedef typename handler_type::param5_type param5_type;
  typedef typename handler_type::param6_type param6_type;

  void SetEnabled(bool enabled)
  {
    _enabled = enabled;
  }
  void SetRecursive(bool recursive)
  {
    _recursive = recursive;
  }
  void Add(const Handler& eventHandler)
  {
    _eventHandlerList.push_back(eventHandler);
  }
  void Remove(const Handler& eventHandler)
  {
    _eventHandlerList.remove(eventHandler);
  }
  void Clear()
  {
    _eventHandlerList.clear();
  }
  bool Contains(const Handler& eventHandler)
  {
    EventHandlerList::iterator it;
    it = std::find(_eventHandlerList.begin(), _eventHandlerList.end(), eventHandler);
    return ( it != _eventHandlerList.end() );
  }
  size_t Count()
  {
    return _eventHandlerList.size();
  }

  void operator()()
  {
    if (!_enabled)
    {
      return;
    }
    if (_eventHandlerList.size() == 0)
    {
      return;
    }
    if ((!_recursive) && _calling)
    {
      return;
    }
    _calling = true;
    for (EventHandlerList::iterator it=_eventHandlerList.begin(); it!=_eventHandlerList.end(); ++it)
      (*it)();
    _calling = false;
  }
  void operator()(param1_type param1)
  {
    if (!_enabled)
    {
      return;
    }
    if (_eventHandlerList.size() == 0)
    {
      return;
    }
    if ((!_recursive) && _calling)
    {
      return;
    }
    _calling = true;
    for (EventHandlerList::iterator it=_eventHandlerList.begin(); it!=_eventHandlerList.end(); ++it)
      (*it)(param1);
    _calling = false;
  }
  void operator()(param1_type param1, param2_type param2)
  {
    if (!_enabled)
    {
      return;
    }
    if (_eventHandlerList.size() == 0)
    {
      return;
    }
    if ((!_recursive) && _calling)
    {
      return;
    }
    _calling = true;
    for (EventHandlerList::iterator it=_eventHandlerList.begin(); it!=_eventHandlerList.end(); ++it)
      (*it)(param1, param2);
    _calling = false;
  }
  void operator()(param1_type param1, param2_type param2, param3_type param3)
  {
    if (!_enabled)
    {
      return;
    }
    if (_eventHandlerList.size() == 0)
    {
      return;
    }
    if ((!_recursive) && _calling)
    {
      return;
    }
    _calling = true;
    for (EventHandlerList::iterator it=_eventHandlerList.begin(); it!=_eventHandlerList.end(); ++it)
      (*it)(param1, param2, param3);
    _calling = false;
  }
  void operator()(param1_type param1, param2_type param2, param3_type param3, param4_type param4)
  {
    if (!_enabled)
    {
      return;
    }
    if (_eventHandlerList.size() == 0)
    {
      return;
    }
    if ((!_recursive) && _calling)
    {
      return;
    }
    _calling = true;
    for (EventHandlerList::iterator it=_eventHandlerList.begin(); it!=_eventHandlerList.end(); ++it)
      (*it)(param1, param2, param3, param4);
    _calling = false;
  }
  void operator()(param1_type param1, param2_type param2, param3_type param3, param4_type param4, param5_type param5)
  {
    if (!_enabled)
    {
      return;
    }
    if (_eventHandlerList.size() == 0)
    {
      return;
    }
    if ((!_recursive) && _calling)
    {
      return;
    }
    _calling = true;
    for (EventHandlerList::iterator it=_eventHandlerList.begin(); it!=_eventHandlerList.end(); ++it)
      (*it)(param1, param2, param3, param4, param5);
    _calling = false;
  }
  void operator()(param1_type param1, param2_type param2, param3_type param3, param4_type param4, param5_type param5, param6_type param6)
  {
    if (!_enabled)
    {
      return;
    }
    if (_eventHandlerList.size() == 0)
    {
      return;
    }
    if ((!_recursive) && _calling)
    {
      return;
    }
    _calling = true;
    for (EventHandlerList::iterator it=_eventHandlerList.begin(); it!=_eventHandlerList.end(); ++it)
      (*it)(param1, param2, param3, param4, param5, param6);
    _calling = false;
  }

private:
  typedef std::list< Handler > EventHandlerList;
  EventHandlerList _eventHandlerList;

  bool _recursive; // can recursively call, default is false;
  bool _calling;
  bool _enabled; // true:broadcast event; false:not droadcast event; default is true;
};

转载于:https://www.cnblogs.com/verycolorful/archive/2011/09/19/2178935.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值