浅谈智能指针的历史包袱

我们学习C++的时候,想必都会听说一个叫智能指针的东西,在初听这个词的时候,我们都难免不被“智能”两个字所吸引,感觉它会比较高级,让人有种忍不住想用一把的冲动。

但事实上,它可能并没有我们所想的那样“智能”、好用,它本质上其实就是一个对象,它去接管了原指针所管理的资源。但这里单单用一个对象就想有“智能”的效果并没有那么容易。在智能指针发展的过程中,出现了Autor_ptr等多种版本的智能指针,但它们都确都或多或少有一些问题存在(相信早期的前辈们会深有感触)。但智能指针的使用性却是不可否认的。它在实际应用中确实是使用很频繁的,因为它可以帮助我们管理资源,防止内存泄漏、处理异常安全问题,是非常方便的。只要我们对它合理的使用。而对于它的实现,它的来源于一种叫RAII的机制,来了解了解。。

RAII机制


RAII机制是通过利用对象出了作用域的自动销毁的机理,使得资源也具有了生命周期,有了自动销毁(自动回收)的功能。RAII全称为Resource Acquisition Is Initialization,它源于C++,在Java,C#,D,Ada,Vala和Rust中也有应用,它是在一些面向对象语言中的一种惯用法。翻译过来它意为资源分配即初始化,定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。RAII要求,资源的有效期与持有资源的对象的生命期严格绑定,即由对象的构造函数完成资源的分配(获取),同时由析构函数完成资源的释放。在这种要求下,只要对象能正确地析构,就不会出现资源泄露问题。

智能指针


RAII在这里就是简单提一下而已,现在我们来看我们今天的主角智能指针。

智能指针(smart pointer)是存储指向动态分配(堆)对象指针的类。它的诞生理由就是,为粗心和懒的人设计的,但是这个设计一定不是反人类的,因为无论你有多厉害只要你是人你总会有犯错误的时候,所以智能指针可以很好地帮助我们,程序员每次 new 出来的内存还需要手动去 delete。当流程比较复杂,就有可能忘记delete了。

不仅仅是这样,异常通常也会导致程序过早退出,进而让delete未得到执行。这时候智能指针就显得格外有用了,因为它们能够确保正确的销毁动态分配的对象。它们也可以用于跟踪被多用户共享的动态分配对象。

事实上,智能指针能够做的还有很多事情,例如处理线程安全(加同步锁),提供写时复制,确保协议,并且提供远程交互服务等一系列类似于“前后需要呼应”起来的操作(如new/delete、malloc/free、fopen/fclose、这里线程安全的lock/unlock等,不过注意的是这些是C++11支持)。其实智能指针只是怕你忘了写上delete、free、fclose...,而专门设置出来的一个对象。所以它是有很强的实用性。

现在我们来看看智能指针的使用。首先一个智能指针至少要符合两点:

①拥有RAII的机制来管理指针。对于编译器来说,智能指针实际上是一个栈对象,并非指针类型,在栈对象生命期即将结束时,智能指针通过析构函数释放它管理的堆内存。

②有指针的基本功能。所有智能指针都重载了“*”和“->”操作符,让它可以直接返回对象的引用以及能用->去操作其指向的对象。若要访问智能指针原来的方法则使用“·”操作符。

③其次就是它还需要考虑深浅拷贝问题(见后面)

现在就可以来模拟标准库中的智能指针实现一种简单的智能指针 auto_ptr

auto_ptr


auto_ptr是第一个版本的智能指针,在C98标准库中就有实现。它的实现思想很简单,就是管理权转移的思想。如下图

代码:

 /*************************************************************************
   > File Name: AutoPtr.cc
   > Author: tp
   > Mail:
   > Created Time: Sun 13 May 2018 05:08:41 PM CST
  ************************************************************************/

 #include <iostream>
 using namespace std;

 struct A
 {
     int age;
     string name;
     A(string na, int a = 0)
         :age(a), name(na)
     { }
 };
 template <class T>
 class AutoPtr
 {
     public:
         AutoPtr(T* ptr)
             :_ptr(ptr) { }

         T& operator*()
         {
             return *_ptr;
         }
         T* operator->()
         {
             return _ptr;
         }
         //ap2( ap1)
         AutoPtr(AutoPtr<T>& a) //防止多次析构,ap2直接夺取管理权,ap1._ptr置空
             :_ptr(a._ptr)
         {
             a._ptr = NULL;
         }
         //ap2 = ap1
         AutoPtr<T>& operator=(AutoPtr<T>& a)
         {
             if(this != &a)
             {
                 delete _ptr;
                 _ptr = a._ptr;
                 a._ptr = NULL;
             }
             return *this;
         }
         ~AutoPtr( )
         {
             cout<<"AutoPtr帮助析构\n";
             delete _ptr;
         }
 protected:
         T* _ptr;
 };
 int main( void)
 {
     AutoPtr<int> ap1(new int(20));
     cout<<++(*ap1)<<endl;

     AutoPtr<A> ap2(new A("napom"));
     cout<<ap2->name<<endl;

     return 0;
 }

这里这两个地方,是需要弄清楚的

然后,看一手结果:

结果感觉还不错,它自动的帮我们做了了空间释放的动作。可是直接夺取管理权的机制那会这么容易让人放心!当我们做一个这样的操作

结果发生了段错误,罪魁祸首就是AutoPtr<int>ap3(ap1)构造拷贝出ap3,ap3完全夺取了ap1的管理权,这个过程中会释放原本属于ap1的那段空间,所以导致ap1无家可归,进行 *ap1访问时程序就会崩溃了。同样道理,当进行了ap3 = ap1,程序也存在这样的问题,原因依旧在于ap1被彻彻底底的夺走了一切,这种编程思想是十分危险的。

源于它的设计思想的缺陷,所以通常一般不推荐使用Autoptr智能指针。 使用了也绝对不能使用"="和拷贝构造。历史在发展,所以我们见到接下来这种想法:

unique_ptr (scoped_ptr)


 由于早期的auto_ptr抢权思想存在严重的缺陷,所以auto_ptr通常是被严禁使用的。但是从C++98到C++11出来期间,C++标准库却迟迟没有给出改良版的智能指针,但是在此期间有一群大牛们就坐不住了,它们在网上讨论,形成了一个叫boost的社区,他们想要自己造一个第三方库,好去解决C++中的一些诟病的地方对c++进行一些完善。这里的scoped_ptr就是当中的一个杰作(后来在C++11出来之后,c++11参考boost版本将scoped_ptr改名为unique_ptr)。它的思想也是特别的直接,它做的就是暴力的防止用户调用拷贝构造和赋值运算符“=”进行夺权管理。它直接将拷贝构造和“=”运算符重载函数给封装起来,以此防止用户来调用或恶意的去实现它。

 /*************************************************************************
   > File Name: ScopedPtr.cc
   > Author: tp
   > Mail:
   > Created Time: Sun 13 May 2018 06:02:24 PM CST
  ************************************************************************/
 #include <iostream>
 using namespace std;

 struct A
 {
     int age;
     string name;
     A(string na, int a = 0)
         :age(a), name( na)
     { }
 };
 template <class T>
 class ScopedPtr
 {
     public:
         ScopedPtr(T* ptr)
             :_ptr(ptr) { }

         T& operator*()
         {
             return *_ptr;
         }
         T* operator->()
         {
             return _ptr;
         }
         ~ScopedPtr( )
         {
             cout<<"AutoPtr析构\n";
             delete _ptr;
         }
 protected:
         ScopedPtr(ScopedPtr<T>& a);
         ScopedPtr<T>& operator=( ScopedPtr<T>& a);
         T* _ptr;

 };

上面的scoped_ptr用于处理单个的对象,而new/delete和new[]/delete[]底层实现机制又是不同的(delete[]头部多开4字节,具体可戳这里)对于new[]出来的多个对象,boost又提供了一个scoped_array版本的智能指针用来管理对象数组,用法和scoped_ptr类似。同样的,上面的auto_ptr也有一个auto_array版本。

scoped_ptr的就好似“你强任你强”,我不给你机会施展,看你怎么夺权管理。这种做法虽然达到了上面的目的。但是这种暴力的思想却带来了新的问题。。由于scoped智能指针独享所有权,当我们真真需要复制智能指针时,需求便满足不了了。如此我们再引入一个更完善智能指针指针,专门用于处理复制,参数传递的情况。这便是如下的shared智能指针。

shared_ptr


这算是我们经常会用到的一种智能指针(它在c++11和boost版本名称一样)。智能指针发展到这一步也就比较成熟了,它已经几乎能够解决平常遇到的问题。它较前面的改进就是添加了引用计数,所以在它的内部就不会出现调用多次析构函数导致的程序崩溃,shared_ptrt同时也不像scoped_ptr那样没有拷贝构造和赋值运算符重载,难以被利用。它每次调用析构时,只需要将引用计数减1即可,当指向该资源的引用计数 *_pCount变成0的时候,才真正的去释放资源。

和前面一样,在boost中也有一个shared_array版本的智能指针用来管理new[]的对象数组。(C++11没有这个版本)

代码:

   > File Name: sharedPtr.cc
   > Author: tp
   > Mail:
   > Created Time: Sun 13 May 2018 06:11:02 PM CST
  ************************************************************************/

 #include <iostream>
 using namespace std;
 struct A
 {
     int age;
     string name;
     A(string na, int a = 0)
         :age(a), name( na)
     { }
 };
 //引用计数版本
 template <class T>
 class SharedPtr
 {
 public:
     SharedPtr( T* ptr)
         :_ptr(ptr)
          ,_pCount( new int(1))
     { }
     T* operator->( )
     {
         return _ptr;
     }
     T& operator*( )
     {
         return *_ptr;
     }
     SharedPtr(const SharedPtr<T>& sp)  //加上const
         :_ptr(sp._ptr)
          ,_pCount(sp._pCount)
     {
         ++(*_pCount);
     }
     SharedPtr<T>& operator=(const SharedPtr<T>& sp)
     {
         if(this != &sp)
         {
             Release( );
             _ptr = sp._ptr;
             _pCount = sp._pCount;
             ++(*_pCount);
         }
         return *this;
     }
     void Release( )
     {
         if(--(*_pCount) == 0)
         {
             cout<<"释放空间"<<endl;
             delete _ptr;
             delete _pCount;
             _ptr = NULL;
             _pCount = NULL;
         }
     }
     ~SharedPtr()
     {
         //cout<<"析构调用"<<endl;
         Release( );
     }
 protected:
     T* _ptr;
     int* _pCount;
 };
 int main( void)
 {
     SharedPtr<A> sp1(new A("gene"));
     cout<<sp1->name<<endl;

     SharedPtr<A> sp2(sp1);
     cout<<sp2->name<<endl;

     SharedPtr<A> sp3(new A("codfish"));
     sp3 = sp2;
     cout<<sp3->name<<endl;

     return 0;
 }

看一下结果:

我们发现它几乎完美的完成了这些功能,确实!这种指针算是比较完美的思想,不过再完美也会有瑕疵,要不然也不会有boost::weak_ptr的存在,

boost::weak_ptr的存在就是为boost::shared_ptr解决一点点瑕疵的。这个瑕疵藏得相对比较深,一般不会遇到的,但是当我们真的遇到的时候,那可能就一个“通宵找Bug的不眠之夜”。还是用例子来说明,

[cpp] view plain

  1. struct Node
  2. {
  3. shared_ptr<ListNode> _prev;
  4. shared_ptr<ListNode> _next;
  5. ListNode(int x)
  6. : _prev(NULL)
  7. ,_next(NULL)
  8. {}
  9. ~Node()
  10. {
  11. cout << "~Node" << endl;
  12. }
  13. };
  14. int main()
  15. {
  16. shared_ptr<Node> cur(new Node(1));
  17. shared_ptr<Node> next(new Node(2));
  18. cur->_next = next;
  19. next->_prev = cur;
  20. cout << "cur.count: " << cur.use_count() << endl;
  21. cout << "next.count: " << next.use_count() << endl;
  22. return 0;
  23. }

现在我们验证shared智能指针的缺陷,我们用系统给我们写的shared_ptr指针,然后再来构造两个双向链表里面的结点,这里这个双向链表可能有一点简陋,但是我们只是需要它的prev和next指针就够了。现在我们运行代码看看会发生什么情况:

明显这里没有调用结点析构,是有问题的。这都源于原来cur和next指针所管理的结点现在都有两个智能指针管理,然后在这里会发生这样一件事:

循环引用一般都会发生在这种"你中有我,我中有你"的情况里面,这里导致的问题就是内存泄漏,这段空间一直都没有释放,现在很明显引用计数在这里就不是很合适了,但是shared_ptr除了这里不够完善,其他的地方表现都令我们比较满意,所以boost社区的大牛们在这里仅是补充了最后一个智能指针week_ptr,

weak_ptr


weak_ptr是为了配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载operator*和->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况.具体一点讲就是,首先weak_ptr 是专门为shared_ptr 而准备的。它是 boost::shared_ptr 的观察者对象,作为观察者,那也就意味着weak_ptr它只对shared_ptr
进行引用却不会去改变其引用计数,当被观察的shared_ptr 失效后,相应的weak_ptr 也相应失效,然后它就什么都不管 光是个删
, 也就是这里的cur和next在析构的时候 ,它都不用引用计数减1(因为这里引用计数一直保持是1,可以直接释放资源) , 直接删除结点就好。这样也就间接地解决了循环引用的问题,当然week_ptr指针的功能不是只有这一个。但是现在我们只要知道它可以解决循环引用就好。

 struct Node
 {
 //  shared_ptr<Node> _prev;
 //  shared_ptr<Node> _next;
     weak_ptr<Node> _prev;
     weak_ptr<Node> _next;
    // Node()
    // :_prev(NULL)
    // ,_next(NULL)
   //  {}
     ~Node()
     {
         cout<<"~Node"<<endl;
     }
 };
 void TestCycleReference( void)
 {
     shared_ptr<Node> cur( new Node);
     shared_ptr<Node> next( new Node);

     cur->_next = next;
     next->_prev = cur;

     cout<<"cur.count:"<<cur.use_count()<<endl;
     cout<<"next.count:"<<next.use_count()<<endl;

 }

对上面shared_ptr优化weak_ptr

关于c++11库中关于weak_ptr的实现接口

1.  拷贝构造

1 constexpr weak_ptr() noexcept;
2
3 weak_ptr (const weak_ptr& x) noexcept;
4 template <class U> weak_ptr (const weak_ptr<U>& x) noexcept;
5
6 template <class U> weak_ptr (const shared_ptr<U>& x) noexcept;

如果一个参数x被传递,而x不是空的,那么弱ptr对象就变成了拥有的x组的一部分,在没有所有权本身的情况下(并且不增加它的使用数量),就可以访问该对象的资产。

如果x是空的,或者如果没有参数传递,构造的弱ptr是空的。

如果x是一个别名,弱ptr保存了所有的数据和存储的指针。

2.operator=重载

1 weak_ptr& operator= (const weak_ptr& x) noexcept;
2 template <class U> weak_ptr& operator= (const weak_ptr<U>& x) noexcept;
3
4 template <class U> weak_ptr& operator= (const shared_ptr<U>& x) noexcept

对象成为拥有的x组的一部分,允许对该对象的资源进行访问,直到过期,而不需要获得所有权本身(并且不增加其引用计数)。

如果x是空的,构造的弱ptr也是空的。

如果x是一个别名,弱ptr保存了所有的数据和存储的指针。

shared_ptr对象可以直接分配给弱ptr对象,但是为了将弱ptr对象分配给sharedptr,它应该使用成员锁(lock)来完成。

包括上面成员,它一共有这些成员

可参考cpulsplus官网:http://www.cplusplus.com/reference/memory/weak_ptr/

定置删除器



在前面用到了的shared_ptr的时候,如果够仔细,你可能注意C++11没有shared_array版本的智能指针,也就是说,对于new[]开出的对象数组,C++11好像没有相应的处理办法了? 当然,这是不可能的。C++11对于此,实际上站在了一个更高的角度来处理。虽然在boost中有_array版本的智能指针管理new[]出来的对象数组,但是实际中还有malloc,fopen..等方式打开资源,单纯的用delete关闭资源显然是力不从心的。所以对此,C++11提供用一个叫删除器的东西来帮助释放资源,它由程序员自己去定置。它的实现其实就是定义一个类出来,然后在类中重载了operator() 在该函数当中进行相应释放资源操作(由于重载了operator(),类就拥有函数的使用特性和功能.所以将这样的类也叫做仿函数)。如我们shared_ptr<int> sp(new int[2]),就可以实现一个这样的类

template<class T>
struct DeleteArray
{
     void operator()(T* ptr)
     {
           delete[] ptr;
     }
} ;

然后可以DeleteArray da定置一个删除器da了 。随后,前面定义智能指针相应改成shared_ptr<int> sp(new int[2], da)即可。

其实它原理也比较简单,为了更方便剖析,我们这里来对上面自定义SharePtr改造,让它支持定置删除功能。

 template<class T>
 struct DelArr
 {
     void operator()(T* ptr)
     {
         cout<<"delete[ ]"<<endl;
         delete[] ptr;
     }
 };
 template <class T, class Del= DelArr<T> >   //为模板参数添加默认值
 class SharedPtr
 {
     public:
         SharedPtr( T* ptr)
             :_ptr(ptr)
              ,_pCount( new int(1))
     { }
         T* operator->( )
         {
             return _ptr;
         }
         T& operator*( )
         {
             return *_ptr;
         }
         SharedPtr(const SharedPtr<T>& sp)  //加上const
             :_ptr(sp._ptr)
              ,_pCount(sp._pCount)
         {
             ++(*_pCount);
         }
         SharedPtr<T>& operator=(const SharedPtr<T>& sp)
         {
             if(this != &sp)
             {
                 Release( );
                 _ptr = sp._ptr;
                 _pCount = sp._pCount;
                 ++(*_pCount);
             }
             return *this;
         }
         void Release( )
         {
             if(--(*_pCount) == 0)
             {
                 if(_ptr)
                 {
                     cout<<"释放空间"<<endl;
                     //delete _ptr;              //_ptr = NULL;                     //_pCount = NULL;
                     Del _del; //定义定置删除仿函数
                     _del(_ptr);  //调用该仿函数
                 }           delete _pCount; 
             }
         }
         ~SharedPtr()
         {
             cout<<"析构调用"<<endl;
             Release( );
         }
     protected:
         T* _ptr;
         int* _pCount;
 };

 void TestSharedPtr()
 {
     //SharedPtr<A, DelArr<A> > sp(new A[2]);
     SharedPtr<int, DelArr<int> >sp1(new int[2]);

 }

实现其实就是在析构时创建一个Del对象,也就是我们前面定置好的删除器的对象。这里Del是Delete,那我们的_del(_ptr)的内部,就是用delete释放_ptr,所以我们就这样把模板和仿函数联系在一起。

而对于malloc、fopen方式打开的资源,就可以这样

 1  class Free
 2  {
 3  public:
 4      void operator()(void* ptr)
 5      {
 6          free(ptr);
 7      }
 8  };
 9  struct FClose
10  {
11      void operator()(void* fp)
12      {
13          fclose((FILE*)fp);
14      }
15  };
16 vod Test()
17  {
18      shared_ptr<void> sp1(malloc(sizeof(int)*3), Free());
19      shared_ptr<FILE> sp2(fopen("test.txt", "r"), FClose());
20 }

最后,智能指针大致原理就是这样,当然这只是本人很粗浅的一点总结,对智能指针使用还有很多东西需要探索。

不过为了平常使用智能指针能更高效的使用,最后小结一下

·在可以使用 boost 库的场合下,拒绝使用 std::auto_ptr,因为其不仅不符合 C++ 编程思想。

·在确定对象无需共享的情况下,使用 boost::scoped_ptr。

·在对象需要共享的情况下,使用 boost::shared_ptr。

·在需要访问 boost::shared_ptr 对象,而又不想改变其引用计数的情况下(循环引用)使用boost::weak_ptr。

附:  

原文地址:https://www.cnblogs.com/tp-16b/p/9033370.html

时间: 2024-11-09 01:46:11

浅谈智能指针的历史包袱的相关文章

再谈智能指针

http://www.cnblogs.com/lewiskyo/p/4214592.html  之前写过一篇文章介绍智能指针,并且简单实现了auto_ptr. 里面提到 auto_ptr 不能做为Stl容器的元素,原因具体是 http://www.360doc.com/content/14/0429/16/1317564_373230218.shtml 简要来说就是在对Stl容器元素进行复制构造时,不能改变原来的值(const T2& value),而auto_ptr在复制构造的时候,一定会修改

浅谈智能家居安防监控的重要性

智能家居监控安防有多重要?有些人将之比作保险,认为出事了有用,没出事则无用武之地,而这种想法也常会形成一个错误的逻辑:家里一般都不出事,所以实际上没什么用.然而,这与不少美国人的想法差距很大,相关调查显示,62%的美国人选择智能家居的目的正是出于家庭安全考虑. 很多人可能不太了解智能家居,对智能家居中的安防更是基本没有概念,事实上智能家居安防在现代家庭中非常有必要.从广义上来讲,智能家居安防主要针对三个方面:一是家庭财产安全:二是家庭人身安全:三是家庭人身健康.那么针对这三个方面,智能家居都是如

浅谈运用指针引用字符串

一.字符串的引用方式 1.如果字符串存放在字符数组中可以用数组名和下标引用 char string[10] = "Hello word"; printf("%s",string); 2.用字符指针变量指向字符串常量,用字符指针引用字符串 char *strp = "Hello word"; printf("%s",strp); //系统将字符串常量以字符数组形式保存在内存中,字符串末尾系统自动加了一个'\0'.再将字符数组的首

浅谈 Objective-C 指针和 Swift2

该译文独家授权给SwiftGG 原文链接:Objective-C Pointers and Swift 2: A Simple Guide 原文日期:2015/08/23 译者:mmoaay 校对:numbbbbb 定稿:shanksyang 本文写于 2015 年 8 月 23 日,与 Xcode7 Beta 版和 Swift 2 兼容 在 Swift 中使用 Objective-C 在 Swift 中读 C 指针 下面这个 Objective-C 方法会返回一个 int 指针,或者说 C 术

浅谈RAII&智能指针

关于RAII,官方给出的解释是这样的"资源获取就是初始化".听起来貌似不是很懂的哈,其实说的通俗点的话就是它是一种管理资源,避免内存泄漏的一种方法.它可以保证在各种情况下,当你对对象进行使用时先通过构造函数来进行资源的分配和初始化,最后通过析构函数来进行清理,有效的保证了资源的正确分配和释放.(特别是在异常中,因为异常往往会改变代码正确的执行顺序,这就很容易引起资源管理的混乱和内存的泄漏) 其中智能指针就是RAII的一种实现模式,所谓的智能就是它可以自动化的来管理它所指向那份空间的资源

C++:浅谈c++资源管理以及对[STL]智能指针auto_ptr源码分析,左值与右值

C++:浅谈c++资源管理以及对[STL]智能指针auto_ptr源码分析 by 小威威 1. 知识引入 在C++编程中,动态分配的内存在使用完毕之后一般都要delete(释放),否则就会造成内存泄漏,导致不必要的后果.虽然大多数初学者都会有这样的意识,但是有些却不以为意.我曾问我的同学关于动态内存的分配与释放,他的回答是:"只要保证new和delete成对出现就行了.如果在构造函数中new(动态分配内存),那么在析构函数中delete(释放)就可以避免内存泄漏了!" 事实果真如此么?

浅谈C++智能指针

操作内存 创建内存:new 过程:用new创建内存,如果成功的话那么直接分配,然后调用对象的构造函数,如果分配不够,那么先去调用用户自己写的set_new_handler函数,一般这个函数是用来释放些内存,然后使多点内存,然后再去申请,如果还是不行,再调用该函数,但是一般就是用个标记变量释放第一次,第二次直接退出,然后会返回一个bad_alloc错误 释放内存:delete 过程:调用析构函数,然后释放内存,如果释放一个数组,那么就是找到数组前几个字节,一般有个保存长度的东西 智能指针 原因:因

浅谈ObjectARX智能指针AcDbObjectPointer的用法

前言 用ARX智能指针打开对象,可以不在乎是否close,但同时也要注意这个变量的作用域(生命周期)问题,ARX智能指针,他的原理是利用类的析构造函数特性自动关闭对象. 这里的智能指针指的是AcDbObjectPointer这一类使用AcDbObjectPointerBase基类派生的类模板统称. 下面是打开示例. void testOpen() { ads_point pt; ads_name ent; if (RTNORM != acedEntSel(NULL,ent,pt)) { retu

经典:浅谈以太坊智能合约的设计模式与升级方法

目录 1. 最佳实践 2. 实用设计案例 2.1 控制器合约与数据合约: 1->1 2.2 控制器合约与数据合约: 1->N 2.3 控制器合约与数据合约: N->1 2.4 控制器合约与数据合约: N->N 2.5 总结 3. 升级 3.1 控制器合约升级,数据合约不升级 3.2 控制器合约不升级,数据合约升级 3.3 控制器合约升级,数据合约升级 4. 数据迁移 4.1 硬编码迁移法 4.2 硬拷贝迁移法 4.3 默克尔树迁移法 以太坊EVM是当前区块链行业应用最为广泛的虚拟机