C ++オブジェクト指向の基本的な知識ポイント

C ++の基本的な知識ポイント

クラスとオブジェクト

クラスメンバー関数

クラスメンバー関数は、クラス定義内の他の変数と同様に、定義とプロトタイプがクラス定義内に記述されている関数を指します。クラスメンバー関数はクラスのメンバーであり、クラスの任意のオブジェクトを操作し、オブジェクト内のすべてのメンバーにアクセスできます。

コード

class Box
{
    public:
    	double length;
    	double breadth;
    	double height;
	void move();
        double getVolume()
        {
            return length * breadth * height;
        }

};

class SmallBox:Box
{
    public:
	void setLength(double len);
	double getLength();
	SmallBox(double len);	//构造函数
	SmallBox(const SmallBox &obj);	//拷贝构造函数
	~SmallBox();		//析构函数
    private:
        double length1;
		double *ptr;
};

コンストラクタ

オブジェクトが作成されたときに実行ます

//构造函数
SmallBox::SmallBox(double len): length1(len)	//初始化列表
{
	//length1 = len;	//初始化列表写法等价于这个
	cout<<"SmallBox对象被创建了"<<endl;
	cout<<"初始化的len:"<<len<<endl;
	ptr = new double;
	*ptr = len;
}

デストラクタ

デストラクタは、作成されたオブジェクトが削除されるたび実行されます

//析构函数会在每次删除所创建的对象时执行
SmallBox::~SmallBox()
{
	cout<<"析构函数执行了,length:"<<*ptr<<endl;
	cout<<"释放内存"<<endl;
	delete ptr;
}

コピー機能

一般的な理解は、クラスを別のクラスに割り当てることです

//如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数
SmallBox::SmallBox(const SmallBox &obj)
{
	cout<<"调用拷贝函数并且为指针ptr分配内存"<<endl;
	//为指针分配内存
	ptr = new double;
	*ptr = *obj.ptr;
}

使用する:

SmallBox sbox1(7.7);

//此时会调用拷贝函数
SmallBox box2 = sbox1;
SmallBox box3 = sbox1;

フレンド機能

フレンドは、フレンド関数と呼ばれる関数にすることができます。フレンドは、フレンドクラスと呼ばfriendキーワードで変更されるクラスにすることもできます。

友達機能を宣言する:

class Box
{
   double width;
public:
   double length;
   friend void printWidth( Box box );
   void setWidth( double wid );
};

フレンド関数の定義:

// 请注意:printWidth() 不是任何类的成员函数
void printWidth( Box box )
{
   /* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */
   cout << "Width of box : " << box.width <<endl;
}

友達クラスを宣言します。

friend class ClassTwo;

インライン関数

関数がインライン関数の場合、コンパイラは関数が呼び出されるすべての場所に関数のコピーを配置します。関数を変更する場合は、再コンパイルする必要があります。そうしないと、古いコードが使用され、インライン関数は次のようになります。定義済み、キーワードインラインは関数名の前配置する必要があり、関数は関数を呼び出す前に定義する必要があります。定義された関数が複数行の場合、コンパイラーはインライン修飾子を無視します

クラス定義で定義されている関数は、インライン指定子が使用されていない場合でも、すべてインライン関数です。

このポインタ

各オブジェクトは、thisポインターを使用して、それ自体を指すことができます。ポインターは、すべてのメンバー関数の暗黙的なパラメーターです。メンバー関数内では、呼び出し元のオブジェクトを指すために使用できます。

フレンドはクラスのメンバーではないため、フレンド関数にはこのポインタがありません。メンバー関数のみがこのポインターを持っています。

サンプルコード:


#include <iostream>
 
using namespace std;
 
class Box
{
   public:
      // 构造函数定义
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
      }
      double Volume()
      {
         return length * breadth * height;
      }
      int compare(Box box)
      {
         return this->Volume() > box.Volume();
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};
 
int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2
 
   if(Box1.compare(Box2))
   {
      cout << "Box2 is smaller than Box1" <<endl;
   }
   else
   {
      cout << "Box2 is equal to or larger than Box1" <<endl;
   }
   return 0;
}

演算結果:

Constructor called.
Constructor called.
Box2 is equal to or larger than Box1

クラスへのポインタ

サンプルコード:

#include <iostream>
 
using namespace std;

class Box
{
   public:
      // 构造函数定义
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
      }
      double Volume()
      {
         return length * breadth * height;
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};

int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2
   Box *ptrBox;                // Declare pointer to a class.

   // 保存第一个对象的地址
   ptrBox = &Box1;

   // 现在尝试使用成员访问运算符来访问成员
   cout << "Volume of Box1: " << ptrBox->Volume() << endl;

   // 保存第二个对象的地址
   ptrBox = &Box2;

   // 现在尝试使用成员访问运算符来访问成员
   cout << "Volume of Box2: " << ptrBox->Volume() << endl;
  
   return 0;
}

の結果

Constructor called.
Constructor called.
Volume of Box1: 5.94
Volume of Box2: 102

クラスの静的メンバー

staticキーワードを使用して、クラスメンバーを静的として定義できます。これを行うと、一般的に使用するオブジェクトの数に関係なく、メモリ内の静的メンバーは常に1つだけになります。つまり、すべてのオブジェクトが同じものを共有します。静的メンバー。

静的メンバーの初期化をクラスの定義に入れることはできません。::を使用してクラスの外部で静的変数を再宣言し、初期化することができます。

静的メンバー変数:

サンプルコード:

#include <iostream>
 
using namespace std;
 
class Box
{
   public:
      static int objectCount;
      Box()
      {
         // 每次创建对象时增加 1
         objectCount++;
      }
};
 
// 初始化类 Box 的静态成员
int Box::objectCount = 0;

int main(void)
{
   Box Box1();    // 声明 box1
   Box Box2();    // 声明 box2
 
   // 输出对象的总数
   cout << "Total objects: " << Box::objectCount << endl;
 
   return 0;
}

結果:

Total objects: 2

静的メンバー関数:

  • +静的関数、クラス名とスコープ解決演算子を使用している限りアクセスできます::
  • 静的メンバー関数は、静的メンバーデータ、その他の静的メンバー関数、およびクラス外の他の関数にのみアクセスできます。
  • 静的メンバー関数にはクラススコープがあり、クラスのthisポインターにアクセスできません。静的メンバー関数を使用して、クラスの特定のオブジェクトが作成されているかどうかを判別できます。

静的メンバー関数と通常のメンバー関数の違い:

  • 静的メンバー関数にはこのポインターがなく、静的メンバー(静的メンバー変数と静的メンバー関数を含む)にのみアクセスできます。
  • 通常のメンバー関数にはこのポインターがあり、クラス内の任意のメンバーにアクセスできますが、静的メンバー関数にはこのポインターがありません。

サンプルコード:


#include <iostream>
 
using namespace std;
 
class Box
{
   public:
      static int objectCount;
      // 构造函数定义
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // 每次创建对象时增加 1
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
      static int getCount()
      {
         return objectCount;
      }
   private:
      double length;     // 长度
      double breadth;    // 宽度
      double height;     // 高度
};
 
// 初始化类 Box 的静态成员
int Box::objectCount = 0;
 
int main(void)
{
  
   // 在创建对象之前输出对象的总数
   cout << "Inital Stage Count: " << Box::getCount() << endl;
 
   Box Box1(3.3, 1.2, 1.5);    // 声明 box1
   Box Box2(8.5, 6.0, 2.0);    // 声明 box2
 
   // 在创建对象之后输出对象的总数
   cout << "Final Stage Count: " << Box::getCount() << endl;
 
   return 0;
}

結果:

Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2

継承

  • 概念

クラスを作成するときに、新しいデータメンバーやメンバー関数を書き換える必要はありません。新しく作成したクラスが既存のクラスのメンバーを継承するように指定するだけです。この既存のクラスは基本クラスと呼ばれ、新しく作成されたクラスは派生クラスと呼ばれます。

基本クラスShapeがあり、Rectangleがその派生クラスであるとします。

// 基类
class Shape 
{...}

// 派生类
class Rectangle: public Shape
{...}
  • アクセス制御と継承

    派生クラスは、基本クラスのすべての非プライベートメンバーにアクセスできます。したがって、基本クラスのメンバーが派生クラスのメンバー関数からアクセスされたくない場合は、基本クラスでプライベートとして宣言する必要があります。

    派生クラスは、次の場合を除いて、すべての基本クラスメソッドを継承します。

    • 基本クラスのコンストラクタ、デストラクタ、およびコピーコンストラクタ。
    • 基本クラスのオーバーロードされた演算子。
    • 基本クラスのフレンド関数。

  • 継承タイプ

    保護された継承プライベート継承、通常はパブリック継承ほとんど使用しませんさまざまなタイプの継承を使用する場合は、次のルールに従ってください。

    • 公共の継承(パブリック):クラスが由来する場合、公開基底クラス、基本クラスのパブリック派生クラスのメンバーのパブリック基本クラスのメンバーが保護するために、派生クラスのメンバーを保護するために、基本クラスののメンバープライベートメンバーことができます直接派生クラスAccessではありませんが基本クラスのパブリックメンバー保護されたメンバーを呼び出すことでアクセスできます
    • 保護された継承(保護された):クラスが基本クラスの保護から派生した場合、基本クラスはパブリックであり、派生クラスのメンバーの保護はメンバーを保護することです。
    • プライベート継承(プライベート):クラスがプライベート基本クラスから派生し場合、基本クラスpublicおよび派生クラスのメンバーの保護はプライベートメンバーになります。
  • 多重継承

    素人の用語では、サブカテゴリには複数の親カテゴリと複数の親カテゴリの特性があります

    構文形式:

    親クラスを区切るにはコンマを使用します

    class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
    {
    <派生类类体>
    };
    

    例:

    // 基类 Shape
    class Shape {...}
    
    // 基类 PaintCost
    class PaintCost {...}
    
    // 派生类
    class Rectangle: public Shape, public PaintCost{...}
    

オーバーロードされた演算子とオーバーロードされた関数

  • 関数のオーバーロードの基本概念

同じ名前で正式なパラメーター(パラメーターの数、タイプ、または順序が異なる)を持つ関数は、オーバーロードを構成します

例:


#include <iostream>
using namespace std;
 
class printData
{
   public:
      void print(int i) {
        cout << "整数为: " << i << endl;
      }
 
      void print(double  f) {
        cout << "浮点数为: " << f << endl;
      }
 
      void print(char c[]) {
        cout << "字符串为: " << c << endl;
      }
};
 
int main(void)
{
   printData pd;
 
   // 输出整数
   pd.print(5);
   // 输出浮点数
   pd.print(500.263);
   // 输出字符串
   char c[] = "Hello C++";
   pd.print(c);
 
   return 0;
}

結果:

整数为: 5
浮点数为: 500.263
字符串为: Hello C++
  • 演算子のオーバーロードの概念

オーバーロードされた演算子は、ある機能を、特別な名前を持つ。関数名は、キーワードのオペレータで構成され、オペレータのシンボルは、後に過負荷状態にすること。他の関数と同様に、オーバーロードされた演算子には戻り値の型とパラメーターリストがあります。素人の言葉で言えば、オペレーターの機能を再定義する

Box operator+(const Box&);

加算演算子を使用して2つのBoxオブジェクトを加算することを宣言します。したがって、これを使用する場合は、2つのBoxオブジェクトを渡す必要があります。

Box operator+(const Box&, const Box&);

例:


#include <iostream>
using namespace std;
 
class Box
{
   public:
 
      double getVolume(void)
      {
         return length * breadth * height;
      }
      void setLength( double len )
      {
          length = len;
      }
 
      void setBreadth( double bre )
      {
          breadth = bre;
      }
 
      void setHeight( double hei )
      {
          height = hei;
      }
      // 重载 + 运算符,用于把两个 Box 对象相加
      Box operator+(const Box& b)
      {
         Box box;
         box.length = this->length + b.length;
         box.breadth = this->breadth + b.breadth;
         box.height = this->height + b.height;
         return box;
      }
   private:
      double length;      // 长度
      double breadth;     // 宽度
      double height;      // 高度
};
// 程序的主函数
int main( )
{
   Box Box1;                // 声明 Box1,类型为 Box
   Box Box2;                // 声明 Box2,类型为 Box
   Box Box3;                // 声明 Box3,类型为 Box
   double volume = 0.0;     // 把体积存储在该变量中
 
   // Box1 详述
   Box1.setLength(6.0); 
   Box1.setBreadth(7.0); 
   Box1.setHeight(5.0);
 
   // Box2 详述
   Box2.setLength(12.0); 
   Box2.setBreadth(13.0); 
   Box2.setHeight(10.0);
 
   // Box1 的体积
   volume = Box1.getVolume();
   cout << "Volume of Box1 : " << volume <<endl;
 
   // Box2 的体积
   volume = Box2.getVolume();
   cout << "Volume of Box2 : " << volume <<endl;
 
   // 把两个对象相加,得到 Box3
   Box3 = Box1 + Box2;
 
   // Box3 的体积
   volume = Box3.getVolume();
   cout << "Volume of Box3 : " << volume <<endl;
 
   return 0;
}

  • オーバーロード可能なオペレーター/オーバーロード不可能なオペレーター

以下は、オーバーロードできる演算子のリストです。

双眼算術演算子 +(加算)、-(減算)、*(乗算)、/(除算)、%(モジュロ)
関係演算子 ==(等しい)、!=(等しくない)、<(より小さい)、>(より大きい>、<=(以下)、> =(以上)
論理演算子 ||(論理OR)、&&(論理AND)、!(論理NOT)
単項演算子 +(正)、-(負)、*(ポインター)、&(アドレスを取る)
インクリメントおよびデクリメント演算子 ++(増加)、–(減少)
ビット演算子 |(ビットごとのOR)、&(ビットごとのAND)、〜(ビットごとの逆)、^(ビットごとのXOR)、、 <<(左シフト)、>>(右シフト)
代入演算子 =、+ =、-=、* =、/ =、%=、&=、| =、^ =、<< =、>> =
宇宙アプリケーションとリリース new、delete、new []、delete []
その他の演算子 ()(関数呼び出し)、->(メンバーアクセス)、、(コンマ)、

以下は、オーバーロードできない演算子のリストです。

  • 。:メンバーアクセス演算子
  • 、->:メンバーポインタアクセス演算子
  • :::ドメイン演算子
  • sizeof:長さ演算子
  • ?::条件付き演算子
  • #:前処理記号

ポリモーフィズム

名前が示すように、動物などの複数の形態を意味します。次のサブカテゴリには、猫、犬、羊などが含まれます。猫、犬、羊は複数の形態の動物です。

C ++ポリモーフィズムとは、メンバー関数が呼び出されると、関数を呼び出すオブジェクトのタイプに応じて異なる関数が実行されることを意味します

サンプルコード:

#include <iostream> 
using namespace std;
 
class Shape {
   protected:
      int width, height;
   public:
      Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      int area()
      {
         cout << "Parent class area :" <<endl;
         return 0;
      }
};
class Rectangle: public Shape{
   public:
      Rectangle( int a=0, int b=0):Shape(a, b) { }
      int area ()
      { 
         cout << "Rectangle class area :" <<endl;
         return (width * height); 
      }
};
class Triangle: public Shape{
   public:
      Triangle( int a=0, int b=0):Shape(a, b) { }
      int area ()
      { 
         cout << "Triangle class area :" <<endl;
         return (width * height / 2); 
      }
};
// 程序的主函数
int main( )
{
   Shape *shape;
   Rectangle rec(10,7);
   Triangle  tri(10,5);
 
   // 存储矩形的地址
   shape = &rec;
   // 调用矩形的求面积函数 area
   shape->area();
 
   // 存储三角形的地址
   shape = &tri;
   // 调用三角形的求面积函数 area
   shape->area();
   
   return 0;
}

次の結果が生成されます。

Parent class area
Parent class area

詳細、上記の出力結果は間違っています。エラーの原因は、呼び出し元の関数area()がコンパイラーによって基本クラスのバージョンとして設定されていることです。これは、いわゆる静的ポリモーフィズム、または静的リンク関数呼び出しです。プログラムが実行される前に準備されています。アーリーバインディングも呼ばれ、Shapeクラスのarea()メソッドの前にvirtualキーワードを追加するだけで済みます。

変更された出力:

Rectangle class area
Triangle class area

この時点で、コンパイラーはポインターのタイプではなく、ポインターの内容を調べます。

各サブクラスには、関数area()の独立した実装があります。これは、ポリモーフィズムが一般的に使用される方法です。ポリモーフィズムを使用すると、すべて同じ名前で実装関数が異なる複数の異なるクラスを作成でき、関数のパラメーターを同じにすることもできます。

  • 仮想機能

    仮想関数は、キーワードvirtualを使用して基本クラスで宣言さた関数です。派生クラスの基本クラスで定義された仮想関数を再定義すると、関数に静的にリンクしないようにコンパイラーに指示します。

  • 純粋仮想関数

    派生クラスの関数を再定義してオブジェクトに適切に適用できるように、基本クラスで仮想関数を定義することもできますが、基本クラスで仮想関数の意味のある実装を提供することはできません。純粋仮想関数は中古。

    仮想関数を定義してはなりません。純粋仮想関数は、派生クラス、つまりインターフェイスの動作を標準化するために使用されます純粋仮想関数を含むクラスは抽象クラスです。抽象クラスはインスタンスを定義できませんが、抽象クラスを実装する具象クラスへのポインターまたは参照を宣言できます。

    基本クラスの仮想関数area()を次のように書き直すことができます。

    class Shape {
       protected:
          int width, height;
       public:
          Shape( int a=0, int b=0)
          {
             width = a;
             height = b;
          }
          // pure virtual function
          virtual int area() = 0;
    };
    
    

    = 0は、関数に本体がなく、上記の仮想関数が純粋仮想関数であることをコンパイラーに通知します

インターフェイス(抽象クラ​​ス)

  • 概念

    クラス内の少なくとも1つの関数が純粋仮想関数として宣言されている場合、このクラスは抽象クラスです。純粋仮想関数は、以下に示すように、宣言で「= 0」を使用して指定されます。

    
    class Box
    {
       public:
          // 纯虚函数
          virtual double getVolume() = 0;
       private:
          double length;      // 长度
          double breadth;     // 宽度
          double height;      // 高度
    };
    
    

    抽象クラスのサブクラスは、すべての純粋仮想関数を書き換える必要があります

  • 抽象クラスの例:

    
    #include <iostream>
     
    using namespace std;
     
    // 基类
    class Shape 
    {
    public:
       // 提供接口框架的纯虚函数
       virtual int getArea() = 0;
       void setWidth(int w)
       {
          width = w;
       }
       void setHeight(int h)
       {
          height = h;
       }
    protected:
       int width;
       int height;
    };
     
    // 派生类
    class Rectangle: public Shape
    {
    public:
       int getArea()
       { 
          return (width * height); 
       }
    };
    class Triangle: public Shape
    {
    public:
       int getArea()
       { 
          return (width * height)/2; 
       }
    };
     
    int main(void)
    {
       Rectangle Rect;
       Triangle  Tri;
     
       Rect.setWidth(5);
       Rect.setHeight(7);
       // 输出对象的面积
       cout << "Total Rectangle area: " << Rect.getArea() << endl;
     
       Tri.setWidth(5);
       Tri.setHeight(7);
       // 输出对象的面积
       cout << "Total Triangle area: " << Tri.getArea() << endl; 
     
       return 0;
    }
    
    

    次の結果が生成されます。

    Total Rectangle area: 35
    Total Triangle area: 17
    

テンプレート

テンプレートは、ジェネリッククラスまたは関数を作成するための青写真または式です。

  • 関数テンプレート

文法形式:

template <typename type> ret-type func-name(parameter list)
{
   // 函数的主体
}

typeは、関数で使用されるデータ型のプレースホルダー名ですこの名前は関数定義で使用できます

例:


#include <iostream>
#include <string>
 
using namespace std;
 
template <typename T>
inline T const& Max (T const& a, T const& b) 
{ 
    return a < b ? b:a; 
} 
int main ()
{
 
    int i = 39;
    int j = 20;
    cout << "Max(i, j): " << Max(i, j) << endl; 
 
    double f1 = 13.5; 
    double f2 = 20.7; 
    cout << "Max(f1, f2): " << Max(f1, f2) << endl; 
 
    string s1 = "Hello"; 
    string s2 = "World"; 
    cout << "Max(s1, s2): " << Max(s1, s2) << endl; 
 
   return 0;
}

  • クラステンプレート

構文形式:

template <class type> class class-name {
.
.
.
}

typeは、クラスがインスタンス化されるときに指定できるプレースホルダータイプ名です。コンマ区切りのリストを使用して、複数の汎用データ型を定義できます。

例:


#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>
 
using namespace std;
 
template <class T>
class Stack { 
  private: 
    vector<T> elems;     // 元素 
 
  public: 
    void push(T const&);  // 入栈
    void pop();               // 出栈
    T top() const;            // 返回栈顶元素
    bool empty() const{       // 如果为空则返回真。
        return elems.empty(); 
    } 
}; 
 
template <class T>
void Stack<T>::push (T const& elem) 
{ 
    // 追加传入元素的副本
    elems.push_back(elem);    
} 
 
template <class T>
void Stack<T>::pop () 
{ 
    if (elems.empty()) { 
        throw out_of_range("Stack<>::pop(): empty stack"); 
    }
    // 删除最后一个元素
    elems.pop_back();         
} 
 
template <class T>
T Stack<T>::top () const 
{ 
    if (elems.empty()) { 
        throw out_of_range("Stack<>::top(): empty stack"); 
    }
    // 返回最后一个元素的副本 
    return elems.back();      
} 
 
int main() 
{ 
    try { 
        Stack<int>         intStack;  // int 类型的栈 
        Stack<string> stringStack;    // string 类型的栈 
 
        // 操作 int 类型的栈 
        intStack.push(7); 
        cout << intStack.top() <<endl; 
 
        // 操作 string 类型的栈 
        stringStack.push("hello"); 
        cout << stringStack.top() << std::endl; 
        stringStack.pop(); 
        stringStack.pop(); 
    } 
    catch (exception const& ex) { 
        cerr << "Exception: " << ex.what() <<endl; 
        return -1;
    } 
}

結果:

7
hello
Exception: Stack<>::pop(): empty stack

おすすめ

転載: blog.csdn.net/weixin_44829930/article/details/108948989