C++Big Three詳細説明+例

33182 ワード


=============単純コンストラクション関数=======
ここでは主にコンストラクション関数について詳しく説明します.ここでは、書く規範を明確にする必要があります.コンストラクション関数を書くときは、thisポインタを使用したほうがいいです.
 1 this->width = width; 
 2 this->height = height; 

このポインタは、等号の左側が現在のクラスのメンバーであることを明確に指摘しています.そうしないと、次のコードに書くと混乱します.
 1 width = width;
 2 height = height; 

もちろん、他の変数の名前を定義して、この曖昧さを変更し、リストを初期化することもできます.
===========コピーコンストラクタ========
まず、コピーコンストラクション関数に現れる共通の病気は、ベースクラスの継承を考慮していないはずです.Shapeベースクラスを書いた以上、無駄に書くことはありません.プログラミングの過程でコードの中のすべての文を重視し、無駄なコードを追加しないようにし、プログラムに現れたすべてのコードを無視しないようにしなければなりません.
次に,コピー構造関数ではコードのロバスト性にも注意し,どのプログラムを記述してもこの問題に注意すべきであり,関数を記述する際に外部コードの変更による本関数の失効やプログラムのクラッシュを防止しなければならない.今回のコピーコンストラクション関数では、leftupのx,yをそれぞれ付与できますが、ポイントクラスの内部を完全に理解している場合に実現することを前提としていますが、ポイントクラスのメンバーが変更されると、作成したコピーコンストラクション関数も失効するため、より合理的なコードは以下の形式に書かれます.
 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は空です.deleteの現在のクラスのleftupを先にしてnullptrを指す必要があります.現在のクラスメンバーleftupが空の場合、otherの判断を続行する必要があります.他の場合は空ですか?leftupは空ではなくメモリを再割り当てし、他の場合はスタックで初期化する必要があります.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されて問題が発生しないようにしたほうがいい.ポインタが空のポインタである場合、解放できる空間がなく、解放されません.一部のコードは表面的には役に立たないように見えますが、良い習慣を身につけなければなりません.そうしないと、バグがどこにあるか分かりません.
=========================補足雑談
1、Singletonモード小談:
C++に依存しすぎてSingletonモードを行うことが望ましいとともに、議論区内にSingletonモードマルチスレッドの実現説明は以下の通りである.
単一スレッドでは、C++は、この内蔵ローカルstaticオブジェクトが最初に呼び出されたときに初期化されることを確認します.しかし、マルチスレッド環境では、この方法は不確実性をもたらし、マルチスレッドの下にいくつかの選択肢がある:(1)マルチスレッドであるが、1つのプロセスに必ず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、三大関数はそれぞれコピー構造関数、コピー付与関数、解析関数であり、ここでは詳細な学習があるはずです.李先生は実は何度も解結合思想に言及して、1つの関数はそれぞれの機能を実現すればいいので、他の関数の内部の内容を操作しないで、下層を操作しないでください.特にチームが協力する時、それぞれのインタフェースを定義して、相応のインタフェースを呼び出して、自分の現在の関数の機能を実現して、他の関数の内部の実現を知る必要はなくて、他の関数の機能にも干渉しないでください.これにより、他のコードが変化した場合、あなたのコードに影響を与えません.つまり、プログラムの汎用性とロバスト性を保証します.