共享对象模板

ⅰ亾dé卋堺 提交于 2020-02-06 18:10:13

================================================================================
标题: 共享对象模板
作者: 叶飞虎
日期: 2018.12.02


   在多线程编程中,涉及多线程对象共享,为了保证对象有效,防止出现一个线程正在操作
对象而另外线程正在或已经释放对象的情况。共享对象模板就是针对多线程共享对象而设计,
通过对象引用计数来保证对象有效及对象释放的向后延迟。

共享对象的头文件(KYShareObj.h)如下:

// =======================================
// Unit   : share object(共享对象)
// Version: 4.0.0.0 (build 2018.12.02)
// Author : Kyee Ye
// Email  : kyee_ye(at)126.com
// Copyright (C) Kyee workroom
// =======================================

#ifndef _KYShareObj_H_
#define _KYShareObj_H_

#include "KYInterlock.h"

// KYLib 2.0 开始使用 KYLib 命名空间
namespace KYLib
{

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/* TKYShare - 共享数据类 */

class TKYShare
{
public:
   // 释放数据的回调函数类型
   typedef void (*TDoFree)(void* AData, int ASize);

   // 共享数据项
   typedef struct
   {
      int         RefCount;            // 引用计数, 注: 不允许外部修改值
      int         Size;                // 数据缓冲区尺寸, 若为 0 表示尺寸未知
      void*       Data;                // 数据指针
      TDoFree     DoFree;              // 释放 Data 的回调函数, 若为 NULL 则调用 free 释放
   } TItem, *PItem;

public:
   // 构造函数
   TKYShare()                          { FItem = NULL; }
   TKYShare(TItem* AItem)              { FItem = _IncRef(AItem); }
   TKYShare(const TKYShare& AValue)    { FItem = _IncRef(AValue.FItem); }
   TKYShare(void* AData, int ASize = 0, TDoFree ADoFree = NULL)
                                       { FItem = _New(AData, ASize, ADoFree); }

   // 析构函数
   ~TKYShare()                         { _Reset(FItem); }

   // 赋值操作
   TKYShare&      operator=(TItem* AItem)
                  { return Set(AItem); }
   TKYShare&      operator=(const TKYShare& AValue)
                  { return Set(AValue); }

   // 类型转换
   operator       const void* () const { return Data(); }
   operator       void* () const       { return Data(); }

   // 取数据指针并返回缓冲区尺寸
   void*          Data(int& ASize) const;

   // 取数据指针
   void*          Data() const         { return FItem != NULL ? FItem->Data : NULL; }

   // 取数据缓冲区尺寸
   // 1. 若 FItem == NULL 则返回值为 -1
   // 2. 若返回值为 0 则表示缓冲区尺寸未知
   int            Size() const         { return FItem != NULL ? FItem->Size : -1; }

   // 判断是否为空/有效
   bool           IsNull() const       { return FItem == NULL; }
   bool           IsValid() const      { return FItem != NULL; }

   // 复位共享数据项
   TKYShare&      Reset()              { _Reset(FItem); return *this; }

   // 设置共享数据项
   TKYShare&      Set(TItem* AItem)    { _Move(FItem, _IncRef(AItem)); return *this; }
   TKYShare&      Set(const TKYShare& AValue)
                  {
                     if (&AValue != this)
                        _Move(FItem, _IncRef(AValue.FItem));
                     return *this;
                  }

   // 设置共享数据项
   TKYShare&      Set(void* AData, int ASize = 0, TDoFree ADoFree = NULL)
                  {
                     _Move(FItem, _New(AData, ASize, ADoFree));
                     return *this;
                  }

   // 替换共享数据项指针, 注: 未加引用 AItem
   TKYShare&      Move(TItem* AItem)   { _Move(FItem, AItem); return *this; }

private:
   TItem*         FItem;               // 共享数据项指针

public:
   // 分配共享数据项
   // 参数:
   //    AData    共享数据指针, 若为 NULL 则返回值为 NULL
   //    ASize    共享数据缓冲区尺寸, 若为 0 表示未知
   //    ADoFree  释放 AData 的回调函数, 若为 NULL 则调用 free 释放
   // 返回值:
   //    NULL     分配失败或 AData == NULL
   //    (非空)   分配成功, 注: 返回值最后必须调用 _DecRef 释放项
   static TItem*  _New(void* AData, int ASize = 0, TDoFree ADoFree = NULL);

   // 共享数据项加引用
   static TItem*  _IncRef(const TKYShare& AShare)
                  { return _IncRef(AShare.FItem); }
   static TItem*  _IncRef(TItem* AItem);
   static TItem*  _IncRef(TItem* AItem, int& ARefCount);

   // 共享数据项减引用, 注: AItem == NULL 则返回值为 -1
   static int     _DecRef(TItem* AItem);
   static int     _DecRefEx(PItem& AItem)
                  {
                     PItem pItem = (PItem)InterlockedExchangePointer((void**)&AItem, NULL);
                     return _DecRef(pItem);
                  }

   // 共享数据项替换/复位
   static void    _Move(PItem& AItem, TItem* ANew)
                  { ANew = (PItem)InterlockedExchangePointer((void**)&AItem, (void*)ANew);
                    _DecRef(ANew); }
   static void    _Reset(PItem& AItem) { _Move(AItem, NULL); }

public:
   // 分配/释放共享数据项的回调函数类型
   typedef TItem* (*TNewItem)();
   typedef void   (*TFreeItem)(TItem* AItem);

   // 初始化共享数据类中分配和释放共享数据项的函数指针
   // 参数:
   //    ANew     分配共享数据项的函数指针, 若为 NULL 则调用 malloc 分配项
   //    AFree    释放共享数据项的函数指针, 若为 NULL 则调用 free 释放项
   // 注:
   // 1. 此函数只能调用一次;
   // 2. 若不调用 _Init 函数则默认使用 malloc 和 free 来分配和释放共享数据项
   static bool       _InitFuncs(TNewItem ANew, TFreeItem AFree);

private:
   static bool       _IsInited;        // 是否已经初始化
   static TNewItem   _FuncNew;         // 分配共享数据项的函数指针
   static TFreeItem  _FuncFree;        // 释放共享数据项的函数指针
};

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/* TKYShareObj - 共享对象类 */

template <class T>
class TKYShareObj
{
private:
   // 重定义项类型
   typedef TKYShare::TItem TItem;

public:
   // 构造函数
   TKYShareObj()                       { FItem = NULL; }
   TKYShareObj(T* AObj, int ATag = 0)  { FItem = TKYShare::_New(AObj, ATag,
                                          (TKYShare::TDoFree)&TKYShareObj<T>::_DoFree); }
   TKYShareObj(const TKYShareObj<T>& AValue)
                                       { FItem = TKYShare::_IncRef(AValue.FItem); }

   // 析构函数
   ~TKYShareObj()                      { TKYShare::_Reset(FItem); }

   // 赋值操作
   TKYShareObj<T>& operator=(const TKYShareObj<T>& AValue)
                  { return Set(AValue); }

   // -> 操作符重载, 注: 共享对象必须有效, 即 FItem != NULL
   T*             operator->() const   { return (T*)FItem->Data; }

   // 类型转换
   operator       const T* () const    { return Obj(); }
   operator       T* () const          { return Obj(); }

   // 取对象指针并返回 tag
   T*             Obj(int& ATag) const;

   // 取对象指针
   T*             Obj() const          { return FItem != NULL ? (T*)FItem->Data : NULL; }

   // 取对象的 tag, 若 FItem == NULL 则返回值为 -1
   int            Tag() const          { return FItem != NULL ? FItem->Size : -1; }

   // 判断是否为空/有效
   bool           IsNull() const       { return FItem == NULL; }
   bool           IsValid() const      { return FItem != NULL; }

   // 复位共享对象
   TKYShareObj<T>& Reset()             { TKYShare::_Reset(FItem); return *this; }

   // 设置共享对象
   TKYShareObj<T>& Set(const TKYShareObj<T>& AValue)
                  {
                     if (&AValue != this)
                        TKYShare::_Move(FItem, TKYShare::_IncRef(AValue.FItem));
                     return *this;
                  }

   // 新建共享对象
   TKYShareObj<T>& New(T* AObj, int ATag = 0)
                  {
                     TItem* pNew = TKYShare::_New(AObj, ATag,
                                       (TKYShare::TDoFree)&TKYShareObj<T>::_DoFree);
                     TKYShare::_Move(FItem, pNew);
                     return *this;
                  }

private:
   TItem*         FItem;

private:
   // 释放对象函数
   static void    _DoFree(T* AObj, int ATag);
};

// 释放对象函数
template <class T>
void TKYShareObj<T>::_DoFree(T* AObj, int ATag)
{
   delete AObj;
}

// 取对象指针并返回 tag
template <class T>
T* TKYShareObj<T>::Obj(int& ATag) const
{
   TItem* pItem = FItem;
   if (pItem != NULL)
   {
      ATag = pItem->Size;
      return (T*)pItem->Data;
   }
   else
   {
      ATag = -1;
      return NULL;
   }
}

}

#endif

共享对象的代码文件(KYShareObj.cpp)如下:

// =======================================
// Unit   : share object(共享对象)
// Version: 4.0.0.0 (build 2018.12.02)
// Author : Kyee Ye
// Email  : kyee_ye(at)126.com
// Copyright (C) Kyee workroom
// =======================================

#include "KYShareObj.h"

// KYLib 2.0 开始使用 KYLib 命名空间
namespace KYLib
{

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/* TKYShare - 共享数据类 */

// ---------------- 内部函数 ----------------

// 分配共享数据项
static TKYShare::TItem* L_NewItem()
{
   return (TKYShare::TItem*)malloc(sizeof(TKYShare::TItem));
}

// 释放共享数据项
static void L_FreeItem(TKYShare::TItem* AItem)
{
   free(AItem);
}

// ---------------- 静态成员 ----------------

// 分配/释放共享数据项的函数指针
bool                 TKYShare::_IsInited  = false;
TKYShare::TNewItem   TKYShare::_FuncNew   = L_NewItem;
TKYShare::TFreeItem  TKYShare::_FuncFree  = L_FreeItem;

// ---------------- 静态函数 ----------------
// 初始化共享数据类中分配和释放共享数据项的函数指针
// 参数:
//    ANew     分配共享数据项的函数指针, 若为 NULL 则调用 malloc 分配项
//    AFree    释放共享数据项的函数指针, 若为 NULL 则调用 free 释放项
// 注:
// 1. 此函数只能调用一次;
// 2. 若不调用 _Init 函数则默认使用 malloc 和 free 来分配和释放共享数据项
bool TKYShare::_InitFuncs(TNewItem ANew, TFreeItem AFree)
{
   // 初始化
   bool result = false;

   // 判断是否未初始化
   if (!_IsInited)
   {
      _IsInited   = true;
      result      = true;

      if (ANew != NULL)
         _FuncNew = ANew;

      if (AFree != NULL)
         _FuncFree= AFree;
   }

   // 返回结果
   return result;
}

// 分配共享数据项
// 参数:
//    AData    共享数据指针, 若为 NULL 则返回值为 NULL
//    ASize    共享数据缓冲区尺寸, 若为 0 表示未知
//    ADoFree  释放 AData 的回调函数, 若为 NULL 则调用 free 释放
// 返回值:
//    NULL     分配失败或 AData == NULL
//    (非空)   分配成功, 注: 返回值最后必须调用 _DecRef 释放项
TKYShare::TItem* TKYShare::_New(void* AData, int ASize, TDoFree ADoFree)
{
   // 初始化
   TItem* result = NULL;

   // 检查参数
   if (AData != NULL)
   {
      result = _FuncNew();
      if (result != NULL)
      {
         result->DoFree    = ADoFree;
         result->Size      = ASize;
         result->Data      = AData;
         result->RefCount  = 1;
      }
   }

   // 返回结果
   return result;
}

// 共享数据项加引用
TKYShare::TItem* TKYShare::_IncRef(TItem* AItem)
{
   // 初始化
   TItem* result = NULL;

   // 检查参数
   if (AItem == NULL)
      ;
   else if (InterlockedIncrement(&AItem->RefCount) > 1)
      result = AItem;
   else
      InterlockedDecrement(&AItem->RefCount);

   // 返回结果
   return result;
}

// 共享数据项加引用
TKYShare::TItem* TKYShare::_IncRef(TItem* AItem, int& ARefCount)
{
   // 初始化
   TItem* result = NULL;

   // 检查参数
   ARefCount = -1;
   if (AItem != NULL)
   {
      ARefCount = InterlockedIncrement(&AItem->RefCount);
      if (ARefCount > 1)
         result = AItem;
      else
         InterlockedDecrement(&AItem->RefCount);
   }

   // 返回结果
   return result;
}

// 共享数据项减引用
int TKYShare::_DecRef(TItem* AItem)
{
   // 初始化
   int result = -1;

   // 检查参数
   if (AItem != NULL)
   {
      result = InterlockedDecrement(&AItem->RefCount);
      if (result == 0)
      {
         // 拷贝项
         void*    pData    = InterlockedExchangePointer(&AItem->Data, NULL);
         int      intSize  = AItem->Size;
         TDoFree  funcFree = AItem->DoFree;

         // 释放项
         _FuncFree(AItem);
         if (pData == NULL)
            ;
         else if (funcFree != NULL)
            funcFree(pData, intSize);
         else
            free(pData);
      }
   }

   // 返回结果
   return result;
}

// ---------------- 构造函数和析构函数 ----------------
// 构造函数
// 析构函数
// ---------------- 私有函数 ----------------
// ---------------- 保护函数 ----------------

// ---------------- 公有函数 ----------------
// 取数据指针并返回缓冲区尺寸
void* TKYShare::Data(int& ASize) const
{
   TItem* pItem = FItem;
   if (pItem != NULL)
   {
      ASize = pItem->Size;
      return pItem->Data;
   }
   else
   {
      ASize = -1;
      return NULL;
   }
}

}

共享对象的示例(test.cpp)如下:

// test.cpp : Defines the entry point for the console application.
//

#include <stdio.h>
#include "KYLib.h"

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

static void L_DoFree(void* AData, int ASize)
{
   printf("~~~ free {data: %p, size: %d}\n", AData, ASize);
}

void Test1()
{
   TKYShare::TItem* pItem = NULL;
   TKYShare objS1;

   printf("{   test1\n");
   printf("... S1 {is-valid: %5s, is-null: %5s, data: %p, size: %d}\n",
          Bool_Str[objS1.IsValid()], Bool_Str[objS1.IsNull()], (const void*)objS1, objS1.Size());

   {
      TKYShare objS2((void*)0x123456, 123, &L_DoFree);
      printf("... S2 {is-valid: %5s, is-null: %5s, data: %p, size: %d}\n",
             Bool_Str[objS2.IsValid()], Bool_Str[objS2.IsNull()], (const void*)objS2, objS2.Size());

      objS1 = objS2;
      printf("... S1 {is-valid: %5s, is-null: %5s, data: %p, size: %d}\n",
             Bool_Str[objS1.IsValid()], Bool_Str[objS1.IsNull()], (void*)objS1, objS1.Size());

      objS2.Reset();
      printf("... S2 {is-valid: %5s, is-null: %5s, data: %p, size: %d}\n",
             Bool_Str[objS2.IsValid()], Bool_Str[objS2.IsNull()], (const void*)objS2, objS2.Size());

      pItem = TKYShare::_IncRef(objS1);
      printf("^^^ item {ref-count: %d, data: %p, size: %d, do-free: %p}\n",
             pItem->RefCount, pItem->Data, pItem->Size, pItem->DoFree);

      objS2.Set(pItem);
      printf("... S2 {is-valid: %5s, is-null: %5s, data: %p, size: %d}\n",
             Bool_Str[objS2.IsValid()], Bool_Str[objS2.IsNull()], (const void*)objS2, objS2.Size());
      printf("^^^ item {ref-count: %d, data: %p, size: %d, do-free: %p}\n",
             pItem->RefCount, pItem->Data, pItem->Size, pItem->DoFree);

      TKYShare objS3((void*)0x3333, 33, &L_DoFree);
      printf("... S3 {is-valid: %5s, is-null: %5s, data: %p, size: %d}\n",
             Bool_Str[objS3.IsValid()], Bool_Str[objS3.IsNull()], (const void*)objS3, objS3.Size());

      objS2.Move(TKYShare::_IncRef(objS3));
      printf("... S2 {is-valid: %5s, is-null: %5s, data: %p, size: %d}\n",
             Bool_Str[objS2.IsValid()], Bool_Str[objS2.IsNull()], (const void*)objS2, objS2.Size());
      printf("^^^ item {ref-count: %d, data: %p, size: %d, do-free: %p}\n",
             pItem->RefCount, pItem->Data, pItem->Size, pItem->DoFree);
   }

   printf("^^^ item {ref-count: %d, data: %p, size: %d, do-free: %p}\n",
          pItem->RefCount, pItem->Data, pItem->Size, pItem->DoFree);

   if (pItem != NULL)
      printf("--- dec-ref {ref-count: %d}\n", TKYShare::_DecRef(pItem));

   printf("}   test1\n");
}

int StrRefCount(const KYString& AStr)
{
   int result = -1;
   char* pStr = (char*)AStr;
   if (pStr != NULL)
      result = ((KYString::TItem*)(pStr - __Offset__(KYString::TItem, Content)))->RefCount;

   return result;
}

typedef TKYShareObj<KYString> TShareStr;

void Test2()
{
   KYString S1 = "1111111";
   KYString S2 = "22222222222222";

   printf("{   test2\n");
   printf("... S1 {ref-count: %d, length: %d, str: %s}\n",
          StrRefCount(S1), S1.Length(), (const char*)S1);
   printf("... S2 {ref-count: %d, length: %d, str: %s}\n",
          StrRefCount(S2), S2.Length(), (const char*)S2);

   {
      KYString* objP1 = new KYString(S1);
      KYString* objP2 = new KYString(S2);

      printf("\nKYString* objP1 = new KYString(S1)\n");
      printf("KYString* objP2 = new KYString(S2)\n");
      printf("... p1 {ref-count: %d, length: %d, obj: %p, str: %s}\n",
             StrRefCount(S1), objP1->Length(), objP1, (const char*)*objP1);
      printf("... p2 {ref-count: %d, length: %d, obj: %p, str: %s}\n",
             StrRefCount(S2), objP2->Length(), objP2, (const char*)*objP2);

      TShareStr objX1(objP1, 111);
      TShareStr objX2(objP2, 222);

      printf("\nTShareStr objX1(objP1, 111)\n");
      printf("TShareStr objX2(objP2, 111)\n");
      printf("... S1 {ref-count: %d, length: %d, str: %s}\n",
             StrRefCount(S1), S1.Length(), (const char*)S1);
      printf("... S2 {ref-count: %d, length: %d, str: %s}\n",
             StrRefCount(S2), S2.Length(), (const char*)S2);

      printf("... x1 {ref-count: %d, length: %d, obj: %p, tag: %d, str: %s}\n",
             StrRefCount(*objX1.Obj()), objX1->Length(), objX1.Obj(), objX1.Tag(), (const char*)*objX1.Obj());
      printf("... x2 {ref-count: %d, length: %d, obj: %p, tag: %d, str: %s}\n",
             StrRefCount(*objX2.Obj()), objX2->Length(), objX2.Obj(), objX2.Tag(), (const char*)*objX2.Obj());

      *objP2 = S1;
      printf("\n*objP2 = S1\n");
      printf("... S1 {ref-count: %d, length: %d, str: %s}\n",
             StrRefCount(S1), S1.Length(), (const char*)S1);
      printf("... S2 {ref-count: %d, length: %d, str: %s}\n",
             StrRefCount(S2), S2.Length(), (const char*)S2);

      printf("... x1 {ref-count: %d, length: %d, obj: %p, tag: %d, str: %s}\n",
             StrRefCount(*objX1.Obj()), objX1->Length(), objX1.Obj(), objX1.Tag(), (const char*)*objX1.Obj());
      printf("... x2 {ref-count: %d, length: %d, obj: %p, tag: %d, str: %s}\n",
             StrRefCount(*objX2.Obj()), objX2->Length(), objX2.Obj(), objX2.Tag(), (const char*)*objX2.Obj());

      *(KYString*)objX2 = S2;
      printf("\n*(KYString*)objX2 = S2\n");
      printf("... S1 {ref-count: %d, length: %d, str: %s}\n",
             StrRefCount(S1), S1.Length(), (const char*)S1);
      printf("... S2 {ref-count: %d, length: %d, str: %s}\n",
             StrRefCount(S2), S2.Length(), (const char*)S2);

      printf("... x1 {ref-count: %d, length: %d, obj: %p, tag: %d, str: %s}\n",
             StrRefCount(*objX1.Obj()), objX1->Length(), objX1.Obj(), objX1.Tag(), (const char*)*objX1.Obj());
      printf("... x2 {ref-count: %d, length: %d, obj: %p, tag: %d, str: %s}\n",
             StrRefCount(*objX2.Obj()), objX2->Length(), objX2.Obj(), objX2.Tag(), (const char*)*objX2.Obj());

      objX1 = objX2;
      printf("\nobjX1 = objX2\n");
      printf("... S1 {ref-count: %d, length: %d, str: %s}\n",
             StrRefCount(S1), S1.Length(), (const char*)S1);
      printf("... S2 {ref-count: %d, length: %d, str: %s}\n",
             StrRefCount(S2), S2.Length(), (const char*)S2);

      printf("... x1 {ref-count: %d, length: %d, obj: %p, tag: %d, str: %s}\n",
             StrRefCount(*objX1.Obj()), objX1->Length(), objX1.Obj(), objX1.Tag(), (const char*)*objX1.Obj());
      printf("... x2 {ref-count: %d, length: %d, obj: %p, tag: %d, str: %s}\n",
             StrRefCount(*objX2.Obj()), objX2->Length(), objX2.Obj(), objX2.Tag(), (const char*)*objX2.Obj());
   }

   printf("... S1 {ref-count: %d, length: %d, str: %s}\n",
          StrRefCount(S1), S1.Length(), (const char*)S1);
   printf("... S2 {ref-count: %d, length: %d, str: %s}\n",
          StrRefCount(S2), S2.Length(), (const char*)S2);
   printf("}   test2\n");
}

// 主函数
int main(int argc, char* argv[])
{
   // 初始化
   InitKYLib(NULL);

   Test1();
   printf("\n");

   Test2();
   printf("\n");

   getchar();
   return 0;
}

共享对象的示例运行结果如下:

{   test1
... S1 {is-valid: false, is-null:  true, data: 00000000, size: -1}
... S2 {is-valid:  true, is-null: false, data: 00123456, size: 123}
... S1 {is-valid:  true, is-null: false, data: 00123456, size: 123}
... S2 {is-valid: false, is-null:  true, data: 00000000, size: -1}
^^^ item {ref-count: 2, data: 00123456, size: 123, do-free: 00401880}
... S2 {is-valid:  true, is-null: false, data: 00123456, size: 123}
^^^ item {ref-count: 3, data: 00123456, size: 123, do-free: 00401880}
... S3 {is-valid:  true, is-null: false, data: 00003333, size: 33}
... S2 {is-valid:  true, is-null: false, data: 00003333, size: 33}
^^^ item {ref-count: 2, data: 00123456, size: 123, do-free: 00401880}
~~~ free {data: 00003333, size: 33}
^^^ item {ref-count: 2, data: 00123456, size: 123, do-free: 00401880}
--- dec-ref {ref-count: 1}
}   test1
~~~ free {data: 00123456, size: 123}

{   test2
... S1 {ref-count: 1, length: 7, str: 1111111}
... S2 {ref-count: 1, length: 14, str: 22222222222222}

KYString* objP1 = new KYString(S1)
KYString* objP2 = new KYString(S2)
... p1 {ref-count: 2, length: 7, obj: 01FE5E60, str: 1111111}
... p2 {ref-count: 2, length: 14, obj: 01FE5E78, str: 22222222222222}

TShareStr objX1(objP1, 111)
TShareStr objX2(objP2, 222)
... S1 {ref-count: 2, length: 7, str: 1111111}
... S2 {ref-count: 2, length: 14, str: 22222222222222}
... x1 {ref-count: 2, length: 7, obj: 01FE5E60, tag: 111, str: 1111111}
... x2 {ref-count: 2, length: 14, obj: 01FE5E78, tag: 222, str: 22222222222222}

*objP2 = S1
... S1 {ref-count: 3, length: 7, str: 1111111}
... S2 {ref-count: 1, length: 14, str: 22222222222222}
... x1 {ref-count: 3, length: 7, obj: 01FE5E60, tag: 111, str: 1111111}
... x2 {ref-count: 3, length: 7, obj: 01FE5E78, tag: 222, str: 1111111}

*(KYString*)objX2 = S2
... S1 {ref-count: 2, length: 7, str: 1111111}
... S2 {ref-count: 2, length: 14, str: 22222222222222}
... x1 {ref-count: 2, length: 7, obj: 01FE5E60, tag: 111, str: 1111111}
... x2 {ref-count: 2, length: 14, obj: 01FE5E78, tag: 222, str: 22222222222222}

objX1 = objX2
... S1 {ref-count: 1, length: 7, str: 1111111}
... S2 {ref-count: 2, length: 14, str: 22222222222222}
... x1 {ref-count: 2, length: 14, obj: 01FE5E78, tag: 222, str: 22222222222222}
... x2 {ref-count: 2, length: 14, obj: 01FE5E78, tag: 222, str: 22222222222222}
... S1 {ref-count: 1, length: 7, str: 1111111}
... S2 {ref-count: 1, length: 14, str: 22222222222222}
}   test2

共享对象模板及示例代码(ShareObj.rar)下载如下:
https://pan.baidu.com/s/1eTWTYD0#list/path=%2Fshares%2Fsources%2F_open

================================================================================
 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!