ITPub博客

首页 > 应用开发 > C/C++ > 细说智能指针

细说智能指针

原创 C/C++ 作者:zhengkojng 时间:2020-08-24 08:44:22 0 删除 编辑

  提到指针,我们就会想到指针的高效,当然,滥用指针也会为我们带来许多的潜在bug。

  提到指针,我们就会想到内存泄漏。比如,使用指针后忘记释放,久而久之,堆空间就会全部使用完,那么会带来很大的危害。再比如,两个指针指向同一片内存区域,我们对同一片区域进行了多次释放,同样会造成内存泄漏。

  为了方便大家的理解,我们先来模拟一下,使用指针却忘记释放带来的危害。首先,我们要定义一个类。这次,还是定义女朋友类吧(之前写过一篇《细说C++的友元》用的就是女朋友类,这次还用这个吧,方便说明问题,更何况我们这群码畜怎么会有女朋友呢,没有女朋友只能自己创建了,哈哈哈哈)。

  女生都喜欢自拍,尤其是漂亮的女生。所以,女生会有很多照片,对吧。那么,我们创建的这个女朋友类,就让她有照片吧。当然了,你女朋友的照片肯定不会随便给别人的吧,所以要把picutre这个变量声明为private类型。既然,女生喜欢自拍,并且发朋友圈,也就是说,其他人虽然得不到她的照片,却可以通过她的朋友圈看到她的自拍,也就意味着我们可以通过一个public函数访问picture这个变量。那么,我们现在来写代码。

  class Girlfriend{

  private:

  int pictures;

  public:

  Girlfriend ( int i ){

  cout << "Girlfriend ( int i ) " << endl;   /*这句代码是不需要的,

  写在这里只是为了后期能够方便我们观察

  */

  this->pictures = i;

  }

  int getPic ( void ){

  return this->pictures;

  }

  ~Girlfriend (){

  cout << "~Girlfriend() " << endl;  /*这句代码是不需要的,

  写在这里只是方便我们观察

  */

  }

  };

  接着,我们来写一个主函数,来使用这个女朋友类。

  int main ( int argc, char** argv ){

  Girlfriend* Alice = new Girlfriend( 100 );

  cout << "my girlfriend's pictures are " << mp->getPic() << endl;

  system ( "pause" );

  return 0;

  }

  运行结果:

  细说智能指针

  我们在主函数中做了什么事情呢?我们通过指针动态创建了一个对象,并且,我创建的这个女朋友Alice有100张照片。看到这里,很多人就会想,指针危险吗?好像没什么危险啊。不是使用正常吗,看到程序运行结果,程序不是完美的执行了嘛,好像也没什么。

  首先,我们创建的这个指针Alice没有去释放,仅仅创建了一个,危害不大,但是多了之后呢。那比如,我们现在再来写一个主函数。

  int main ( int argc, char** argv ){

  Girlfriend* Alice = new Girlfriend( 100 );

  Girlfriend* Lisa = new Girlfriend( 200 );

  cout << "my girlfriend Alice's pictures are " << Alice->getPic() << endl;

  cout << "my girlfriend Lisa's pictures are " << Lisa->getPic() << endl;

  system ( "pause" );

  return 0;

  }

  运行结果:

  细说智能指针

  我们先抛开指针不谈。我们就来谈谈女朋友。假如Alice是你女朋友,你手机里有她的100张照片,后来你们因为一些事情产生了矛盾分手了,于是你交了另外一个女朋友Lisa。有一次,Lisa翻你的手机,发现你竟然有100张前女友照片,她什么感受,估计要疯了,你说后果严不严重,所以,趁你的现女友没发现之前,赶紧把前女友照片给删了,赶紧把前女友照片释放掉,不然,后果……

  现在,回归正题,我们使用指针创建对象,一个,两个,都没问题,那如果多了呢?100个,1000个,10000个,那内存还能受的了吗?所以,用完指针之后一定要释放指针。

  但是,我们是人啊,不是机器,总会有遗忘的时候,那么我们有没有办法在指针使用完毕后,它自己释放呢?当有了这个需求后,我们就要开始思考解决办法了。很快,我们就有了解决方案:通过类对象模拟指针。指针有两个运算符,一个是->,另一个是星号,只要在类内重载这两个操作符就行了。那么,这个类的成员变量是什么呢?就是一个指针。通过类来模拟指针的,这就是智能指针了。现在,我们先来写一个简陋版的。

  class SmartPointer{

  private:

  Girlfriend* sp;

  public:

  SmartPointer ( Girlfriend* p = NULL ){

  sp = p;

  }

  Girlfriend* operator -> (){

  return sp;

  }

  Girlfriend& operator * (){

  return *sp;

  }

  ~SmartPointer (){

  delete sp;

  cout << "~SmartPointer() " << endl;  /*这句代码完全没必要,

  写在这里只是便于我们观察

  */

  }

  }

  我们在这个智能指针类中,重载指针了指针的两个运算符。通过这样做,我们解决了使用完毕指针后自动释放的问题。那么,还有一个问题,如果两个指针指向同一片区域,这样在释放指针时也会造成内存泄漏,因为同一片区域被释放两次。这个问题怎么解决呢?通过重载拷贝构造函数和赋值操作符。我们现在类内实现拷贝构造函数。

  SmartPointer ( const SmartPointer& obj ){

  sp = obj.sp;

  const_cast<SmartPointer&>(obj).sp = NULL;

  }

  我们实现了拷贝构造函数,因为同一片区域只能有一个指针指向,所以,我们在把obj指向的地址赋给sp后,要将obj的sp赋值为NULL。这里用到了一个强制类型转换。

  现在,我们要在类内实现重载赋值操作符。

  SmartPointer& operator = ( const SmartPointer& obj ){

  if ( this != &obj ){

  delete sp;

  sp = obj.sp;

  obj.sp = NULL;

  }

  return *this;

  }

  到这里为止,我们就把智能指针类实现完毕了。同时,使用智能指针还可以避免指针比较或加减运算,因为这些运算会造成指针越界带来的bug。好了,我们看一下,运行结果,符不符合我们的预期。

  细说智能指针

  哇,非常符合。指针使用完毕后完美释放。

  下面是完整代码:

  #include <iostream>

  #include <string>

  using namespace std;

  class Girlfriend{

  private:

  int pictures;

  public:

  Girlfriend ( int i ){

  this->pictures = i;

  cout << "Girlfriend ( int i )" << endl;

  }

  int getPic ( void ){

  return this->pictures;

  }

  ~Girlfriend (){

  cout << "~Girlfriend ()" << endl;

  }

  };

  class SmartPointer{

  private:

  Girlfriend* sp;

  public:

  SmartPointer ( Girlfriend* p = NULL ){

  sp = p;

  cout << "SmartPointer ( Girlfriend* p = NULL )" << endl;

  }

  Girlfriend* operator -> (){

  return sp;

  }

  Girlfriend& operator * (){

  return *sp;

  }

  SmartPointer ( const SmartPointer& obj ){

  sp = obj.sp;

  const_cast<SmartPointer&>(obj).sp = NULL;

  }

  SmartPointer& operator = ( const SmartPointer& obj ){

  if ( this != &obj ){

  delete sp;

  sp = obj.sp;

  const_cast<SmartPointer&>(obj).sp = NULL;

  }

  return *this;

  }

  bool isnull (){

  return ( sp == NULL );

  }

  ~SmartPointer (){

  cout << "~SmartPointer()" << endl;

  delete sp;

  }

  };

  int main ( int argc, char** argv ){

  SmartPointer Alice = new Girlfriend( 100 );

  SmartPointer Lisa = new Girlfriend( 200 );

  cout << "my girlfriend Alice's pictures are " << Alice->getPic() << endl;

  cout << "my girlfriend Lisa's pictures are " << Lisa->getPic() << endl;

  system ( "pause" );

  return 0;

  }

  后记:

  这篇文章写完也是花了我两个小时了,这其中的代码都是经过我自己电脑测试的。虽然写这篇文章只花了我两个小时,但是,我在写这篇文章之前就已经开始在构思了,该如何写,才能做到通俗易懂。看来真的,想写好一篇文章还是比较困难的,毕竟要把自己学会的东西,通过语言文字描述出来,还是不容易的。之前写的一篇《细说C++的友元》上了博客的推荐,这里非常感谢51cto的小编。因为,那篇文章的缘故,我尽心尽力的写了这篇文章。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69934487/viewspace-2714001/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2020-08-24

  • 博文量
    10
  • 访问量
    36399