zoukankan      html  css  js  c++  java
  • C++ Big Three详细讲解+示例

    =========简单构造函数=========

            ​这里主要详细介绍了构造函数,这里只需要说明一点,就是要明确一下写作规范,书写构造函数时最好使用this指针,即:

     1 this->width = width; 
     2 this->height = height; 

    this指针明确指出了等号左边是当前类的成员,否则写成下面代码会让人混乱。

     1 width = width;
     2 height = height; 

    当然你也可以定义其他变量名字,改变这种歧义,并同时进行初始化列表。

    =========拷贝构造函数=========

            ​首先,拷贝构造函数中出现的通病应该是没有考虑到基类的继承,既然写了Shape基类,就不会白写,应该在编程的过程中重视代码中的每一条语句,要做到不添加无用代码,也不忽视程序中出现的每一句代码。

            ​其次,拷贝构造函数中还要注意代码的鲁棒性,编写任何程序都应该注意这个问题,编写函数时要防止外部代码改变导致本函数失效或者导致程序崩溃。在本次拷贝构造函数中,你可以对leftup中的x,y分别进行赋值,但前提是在你对Point类内部完全了解的情况下进行实现的,但是如果Point类中成员发生改变,你编写的拷贝构造函数也会相应失效,所以更合理的代码是写成下列形式:

     1 this->leftup = new Point(*(other.leftup)); 

            ​接下来说说拷贝构造的顺序,这也是令人容易忽略的地方,

     1 inline 2 Rectangle::Rectangle(const Rectangle& other):Shape(other),width(other.width),height(other.height) 

        ​    ​这里也不费口舌详细解释了,大家也应该知道了,拷贝构造的顺序与你书写的顺序没有关系,无论你写成什么顺序,编译器里已经约定好顺序,即先父类,后原类中对数据定义的顺序。因此,这里的建议就是为了阅读代码方便,书写顺序最好与拷贝构造的顺序一致。

            ​最后,说说空指针的问题,如果你要拷贝构造的other是空指针,就没必要在堆中再创建分配,只能是浪费空间。因此,拷贝构造函数如下:

    1 inline
    2  Rectangle::Rectangle(const Rectangle& other):Shape(other),width(other.width),height(other.height){
    3      if(other.leftup != nullptr){
    4          this->leftup = new Point(*(other.leftup));
    5      }
    6      else{
    7          this->leftup = nullptr;
    8      }
    9  }

    =========拷贝赋值函数=========

            ​拷贝赋值函数首先要做的就是自检,如果本身自己赋值,直接返回。

    1 if(this ==& other)
    2 
    3 {
    4      return *this;
    5  }

            ​同时,拷贝赋值函数也同样需要考虑基类的继承,应该写成这样的形式:

     1 Shape::operator=(other); 

            ​这里就是把“operator=”看做一个整体,即Shape的成员函数,然后我们直接传入参数other,这样便调用了shape的默认构造函数,对no进行的赋值操作,这样做的好处是我们完全不必管Shape内部是如何实现的,只要做我们的赋值就可以了。

            ​最后,拷贝赋值需要考虑other.leftUp指针和自身leftup指针为空的4种情况。首先我们要判断leftup是否为空,如果当前类成员leftup不为空的情况下,继续判断other.leftup是否为空,如果other.leftup不为空直接进行赋值,如果other.leftup为空,需要先delete当前类中的leftup,然后将其指向nullptr;如果当前类成员leftup为空的情况下,仍需要继续判断other.leftup是否为空,如果other.leftup不为空需要重新分配内存,并同时在堆中初始化,如果other.leftup为空,不需要任何操作。

     1 if(leftup != nullptr)
     2  {
     3      if(other.leftup != nullptr)
     4      {
     5          *leftup = *(other.leftup);
     6      }
     7      else
     8      {
     9         delete leftup;//caution: memory leak
    10         leftup = nullptr;
    11      }
    12  }
    13  else
    14  {
    15      if(other.leftup != nullptr)
    16      {
    17          leftup = new Point(*(other.leftup));
    18      }
    19  }

        ​    ​这里需要说明一点:只要代码能够表述上述意思即可,也可以先判断other.leftup,再判断leftup,效果是一样的。

    ===========析构函数===========

            ​析构函数比较简单,就是需要注意一点:

    1 inline
    2  Rectangle::~Rectangle()
    3  {
    4      delete leftup;
    5      leftup = nullptr;
    6  }

            ​delete只是对指针的指向空间的释放,并不会改变指针的值,即指针不为空。指针的本身内容,即指向空间的地址,是没有发生变化的。同时,C++是可以delete空指针的,C++不能直接delete的是野指针,是会出问题的,所以一般指针被delete之后,最好立即赋值为空,以免被再次delete而出现问题。当指针为空指针时,没有空间可释放,也就不去释放了。有些代码表面看起来没用,但是要养成好习惯,否则bug出现的时候都不知道错误在哪里。

    ===========补充杂谈===========

    1、Singleton模式小谈

            ​最好太依赖C++做Singleton模式,同时在讨论区内有Singleton模式多线程的实现说明如下:

            ​在单线程下,C++确保这种内置的本地static对象在首次被调用时被初始化。但是在多线程环境下,这种做法会带来不确定性,在多线程下面有几种选择:(1) 虽然是多线程,但是一个进程中一定有一个主进程并且首先被执行,可以沿用meyers的实现方式,并在主线程里面初始化所有的Instance以确保在单线程环境下的Instance。也就是说,你要在主线程启动后,首先调用雷氏A::GetInstance()的函数返回本地静态引用;(2) 可以采用所谓“lock+double check”的方法,可以写成下列形式

    1.  #include 
    
    2.  static A* m_pInstance = NULL;
    
    3.  std::mutex Mutex; // mutex for critical section
    
    4.    
    
    5.  static A& A::GetInstance()
    
    6.  {
    
    7.    if (m_pInstance == NULL)
    
    8.        {
    
    9.            std::lock_guard lock(Mutex); //此处锁住临界区对象
    
    10.          if (m_pInstance == NULL)
    
    11.             { 
    
    12.                   // 此处再次检查m_pInstance,并初始化
    
    13.                   m_pInstance = new A();
    
    14.             }
    
    15.       }
    
    16.       return *m_pInstance;
    
    17.}

    使用:

    1.  A anInstance = A::GetInstance();
    
    2.  anInstance.SomeMethod();

    题目描述:

    为 Rectangle 类实现构造函数,拷贝构造函数,赋值操作符,析构函数。

    程序编写:

      1 //============Rectangle.h===============
      2 
      5 #ifndef _RECTANGLE_
      6  #define _RECTANGLE_
      9 
     10 // forward declaration
     11  #include
     12  #include
     13  using namespace std;
     14 
     18  //class declaration
     19  class Shape
     20  {
     21  public:
     22      Shape() {no = ++cnt;}
     23      Shape(const Shape& other) { no= other.no; ++cnt;}
     24      Shape& operator=(const Shape& other) { no = other.no; return *this; }
     25      virtual ~Shape() {--cnt;}
     26  private:
     27      int no;
     28      static int cnt;
     29  };
     30  int Shape::cnt = 0;
     31 
     32  
     33 
     34 class Point
     35  {
     36      int x;
     37      int y;
     38  public:
     39      Point(int x=0,int y=0)
     40      {
     41          this->x = x;
     42          this->y = y;
     43      }
     44      int get_x() const {return x;}
     45      int get_y() const {return y;}
     46  };
     47  class Rectangle: public Shape
     48  {
     49      int width;
     50      int height;
     51      Point* leftup;
     52  public:
     53      Rectangle(int width,int height,int x,int y);
     54      Rectangle(const Rectangle& other);
     55      Rectangle& operator=(const Rectangle& other);
     56      ~Rectangle();
     57      int Girth() const {return (width+height)*2;}
     58      int Area() const {return width*height;}
     59      int get_width() const {return width;}
     60      int get_height() const {return height;}
     61      Point* get_leftup() const {return leftup;}
     62  };
     63  //class definiition
     64 
     65  inline
     66  Rectangle::Rectangle(int width=0,int height=0,int x=0,int y=0):leftup(new Point(x,y))
     67  {
     68      this->width = width;
     69      this->height = height;
     70  }
     71 
     72  inline
     73  Rectangle::~Rectangle()
     74  {
     75      delete leftup;
     76      leftup = nullptr;
     77  }
     78 
     79  inline
     80  Rectangle::Rectangle(const Rectangle& other):Shape(other),width(other.width),height(other.height){
     81      if(other.leftup != nullptr){
     82          this->leftup = new Point(*(other.leftup));
     83      }
     84      else{
     85          this->leftup = nullptr;
     86      }
     87  }
     88 
     89  inline Rectangle&
     90  Rectangle:: operator=(const Rectangle& other){
     91 
     92  
     93 
     94     if(this == &other){//check self assignment
     95          return *this;
     96      }
     97      Shape::operator=(other);
     98      width = other.width;
     99      height = other.height;
    100 
    101  
    102 
    103     if(leftup != nullptr)
    104      {
    105         if(other.leftup != nullptr)
    106          {
    107              *leftup = *(other.leftup);
    108          }
    109          else
    110          {
    111             delete leftup;//caution: memory leak
    112             leftup = nullptr;
    113          }
    114      }
    115      else
    116      {
    117          if(other.leftup != nullptr)
    118          {
    119              leftup = new Point(*(other.leftup));
    120          }
    121      }
    122      return *this;
    123  }
    124  //output
    125  ostream& operator<<(ostream& os,const Rectangle& other)
    126  {
    127      os<<": width("<<other.get_width()<<"),"
    128     << "height("<<other.get_height()<<"),"
    129     << "leftup.x("<<other.get_leftup()->get_x()<<"),"
    130     << "leftup.y("<<other.get_leftup()->get_y()<<"),"<<endl
    131     << "       Girth="<<other.Girth()<<","
    132      <<" Area="<<other.Area()<<"."<<endl;
    133      return os;
    134  }
    135 
    136  
    137 
    138 #endif // _RECTANGLE_
    139 
    140 // ============Rectangle.cpp=============
    141 
    142  
    143 
    144 #include "Rectangle.h"
    145 
    146 int main()
    147  {
    148      Rectangle rec0;
    149      cout<<"rec0()"<<rec0<<endl;
    150      Rectangle rec1(3,4,0,0);
    151      Rectangle rec2(5,6,4,4);
    152      cout<<"rec1()"<<rec1<<endl;
    153      cout<<"rec2()"<<rec2<<endl;
    154 
    157     cout<<"----------Copy creator rec3(rec2)---------"<<endl;
    158      Rectangle rec3(rec2);
    159      cout<<"rec3()"<<rec3<<endl;
    160 
    163     cout<<"----------Copy operator rec3=rec1---------"<<endl;
    164      rec3=rec1;
    165      cout<<"rec3()"<<rec3<<endl;
    169 cout<<"----------Copy operator rec3=rec3---------"<<endl; 170 rec3=rec3; 171 cout<<"rec3()"<<rec3<<endl; 172 }

    题目总结:

    1、在Shape基类中,静态私有数据成员需要在类外面定义初始化。同时,Shape类中的函数可以根据相应要求变化。

    2、程序中验证了默认构造函数、拷贝构造、拷贝赋值以及自赋值,同时实现了周长和面积的计算。

    3、三大函数分别为拷贝构造函数、拷贝赋值函数和析构函数,到此应该有一个详细的学习。李老师其实提到了很多次解耦思想,一个函数只要实现各自的功能即可,不要去操作其他函数内部的内容,不要去操作底层。尤其是团队合作的时候,定义好各自的接口,调用相应的接口,实现自己当前函数的功能,并不需要知道其他函数的内部实现,也不要干涉其他函数的功能。这样在其他代码变化时,才不会影响你的代码,即保证了程序的通用性和鲁棒性。

    转载请注明出处: C++博客园:godfrey_88 http://www.cnblogs.com/gaobaoru-articles/
  • 相关阅读:
    hdu 1425 sort 解题报告
    codeforces B. Jeff and Periods 解题报告
    codeforces A. Jeff and Digits 解题报告
    codeforces B. Xenia and Spies 解题报告
    Python 条件判断的使用
    Python Apache日志处理脚本(初稿)
    Python函数定义
    Python多条件配合使用
    Python循环的使用(2)
    NavigationController导航栏中添加多个UIBarButtonItem
  • 原文地址:https://www.cnblogs.com/gaobaoru-articles/p/5236282.html
Copyright © 2011-2022 走看看