オブジェクト指向プログラミング (OOP) はプログラミング パラダイムであり、その主要な基本概念はクラス (Class) とオブジェクト (Object) です。このプログラミング パラダイムは C++ で広く使用されています。オブジェクト指向の考え方では、オブジェクトをプログラムの基本単位と考え、オブジェクトにはデータとデータを操作する関数が含まれます。
1. オブジェクト指向とプロセス指向の違い
オブジェクト指向は、人間の思考習慣に合わせたプログラミングの考え方です。現実にはさまざまなものがさまざまな形で存在しており、それらの間にはさまざまなつながりがあります。プログラム内でオブジェクトを使用して現実の物体をマッピングし、オブジェクト間の関係を使用して物体のつながりを記述するこの種の考え方がオブジェクト指向です。
プロセス指向とは、問題を解決するために必要な手順を分析し、それらの手順を 1 つずつ実現する関数を使用し、使用するときに 1 つずつ呼び出します。
オブジェクト指向はプロセス指向とは異なり、問題を構成するものを一定の規則に従って複数の独立したオブジェクトに分割し、そのオブジェクトのメソッドを呼び出すことで問題を解決します。もちろん、アプリケーションには複数のオブジェクトが含まれており、複数のオブジェクトが連携することでアプリケーションに必要な機能を実現できるため、アプリケーションの機能が変更された場合でも、個々のオブジェクトを変更するだけでコードを作成することができます。メンテナンスが容易になります。
2. C++ のオブジェクト指向プログラミングの考え方には、次の 3 つの特徴があります。
1. カプセル化: カプセル化はオブジェクト指向プログラミングの重要な機能であり、データ (変数) とメソッド (関数) をバインドしてデータを操作し、オブジェクトと呼ばれる全体を形成することができます。
C++ では、クラスはカプセル化のための主要なツールです。クラスは、データ メンバー (メンバー変数) とメンバー関数を含む新しい型を定義します。クラスの外では、クラスのデータ メンバーに直接アクセスすることはできず、データの隠蔽と保護を実現するメンバー関数を介してのみアクセスできます。この非表示と保護のレベルは、アクセス修飾子 (プライベート、プロテクト、パブリック) によって調整できます。
class MyClass {
private:
int myVariable; // 私有数据成员,只能在类的成员函数中直接访问
public:
void setMyVariable(int value) { // 公有成员函数,可以从类的外部调用
myVariable = value;
}
int getMyVariable() { // 公有成员函数,可以从类的外部调用
return myVariable;
}
};
この例では、myVariable
これはプライベートであり、クラスの外部から直接アクセスすることはできません。setMyVariable
と のgetMyVariable
2 つのパブリック メンバー関数を通じてのみ、 の値を設定および取得できますmyVariable
。
カプセル化には主に次の 2 つの利点があります。
① セキュリティの向上: クラスの内部実装の詳細を隠すことで、外部コードがクラスの内部データを誤って処理するのを防ぐことができます。
② 保守性の向上:外部コードはクラスのパブリックインターフェースを介してクラスと対話するため、クラスの内部実装が変更されても外部コードには影響しません。
したがって、カプセル化は非常に重要なプログラミング スキルであり、堅牢で保守可能なコードを構築するための鍵となります。
2. 継承: C++ では、継承は、1 つまたは複数の既存のクラス (基本クラスと呼ばれる) の属性と動作を継承する新しいクラス (派生クラスと呼ばれます) を作成するためのメカニズムです。
継承の主な目的はコードの再利用ですが、型の一般化と特殊化も目的としています。一般化とは、複数のクラスから共通部分を抽出して、より一般的なクラスを形成することを指します。このプロセスは、共通基本クラスの抽出とも呼ばれます。特殊化とは、基本クラスに基づいて新しい属性と動作を追加して、より具体的なクラスを形成することです。
例として次の例を考えてみましょう。
class Animal {
public:
void eat() {
cout << "I can eat!" << endl;
}
void sleep() {
cout << "I can sleep!" << endl;
}
};
class Dog : public Animal {
public:
void bark() {
cout << "I can bark! Woof!" << endl;
}
};
この例では、Animal
基本クラスDog
は派生クラスです。Dog
クラスが public を通じてクラスを継承するとAnimal
、Dog
そのクラスはAnimal
クラスのすべての public メンバーと protected メンバーを継承します。これは、Dog
クラスのオブジェクトがAnimal
クラスのオブジェクトと同じように食べる ( eat
) およびスリープする ( ) ことができるsleep
一方で、Dog
クラスは ( bark
) と呼ばれる新しい動作も追加することを意味します。
C++ には、パブリック継承 (public)、保護継承 (protected)、プライベート継承 (プライベート) の 3 つのタイプの継承があることに注意してください。
これらの主な違いは、派生クラスの基本クラスのメンバーへのアクセス レベルです。パブリック継承が最も一般的に使用され、基本クラスの public メンバーと protected メンバーは派生クラスで元のアクセス レベルを維持します。保護された継承とプライベートな継承は比較的まれに使用され、基本クラスのパブリック メンバーと保護されたメンバーをそれぞれ派生クラスの保護されたメンバーとプライベート メンバーに変換します。
さらに、C++ は多重継承をサポートしています。これは、派生クラスが複数の基本クラスを持つことができることを意味します。ただし、多重継承は複雑な問題 (ダイアモンド継承問題など) を引き起こす可能性があるため、クラスの継承関係を設計する場合は注意して多重継承を使用する必要があります。
3. ポリモーフィズム: ポリモーフィズムはオブジェクト指向プログラミングにおける重要な概念であり、これにより、さまざまな種類のオブジェクトを統一した方法で処理できるようになります。C++ では、ポリモーフィズムは主に、コンパイル時ポリモーフィズム (静的ポリモーフィズム) と実行時ポリモーフィズム (動的ポリモーフィズム) の 2 つの形式で現れます。
①コンパイル時ポリモーフィズム:主に関数のオーバーロードとテンプレートによって実現されます。関数のオーバーロードを使用すると、複数の関数が同じ関数名で異なるパラメーター リストを持つことができ、コンパイラは関数のパラメーター リストに基づいてコンパイル時にどの関数を呼び出すかを決定します。テンプレートを使用すると、関数またはクラスが複数のデータ型を操作できるようにスキーマを定義できます。
関数のオーバーロードとは、パラメーター リストが異なる限り、同じスコープ内に同じ名前の複数の関数が存在できることを意味します。コンパイラは、受け取ったパラメータの型と数に基づいて、関数のどのオーバーロードされたバージョンを呼び出すかを決定します。この種のポリモーフィズムはコンパイル段階で発生するため、コンパイル時ポリモーフィズムと呼ばれます。
例えば:
void print(int a) {
std::cout << "Printing int: " << a << std::endl;
}
void print(double a) {
std::cout << "Printing double: " << a << std::endl;
}
void print(const std::string& str) {
std::cout << "Printing string: " << str << std::endl;
}
int main() {
int a = 5;
double b = 3.14;
std::string c = "Hello, World!";
print(a); // 调用 print(int) 函数
print(b); // 调用 print(double) 函数
print(c); // 调用 print(const std::string&) 函数
return 0;
}
テンプレートはC++ の汎用プログラミング機能であり、これを使用すると、データ型ごとに同じコードを書き直すことなく、汎用関数またはクラスを定義できます。テンプレートがコンパイルされると、実際に使用される型に応じて、対応する具体的な実装が生成されます。この種のポリモーフィズムはコンパイル段階でも発生するため、コンパイル時ポリモーフィズムにも属します。テンプレートは、関数テンプレートとクラス テンプレートに分類できます。
例えば:
関数テンプレート:
template <typename T>
T add(T a, T b) {
return a + b;
}
int main() {
int a = 1, b = 2;
double c = 1.5, d = 2.5;
std::cout << add(a, b) << std::endl; // 调用 add<int> 函数
std::cout << add(c, d) << std::endl; // 调用 add<double> 函数
return 0;
}
クラステンプレート:
template <typename T>
class MyContainer {
public:
MyContainer(T item) : item_(item) {}
T getItem() {
return item_;
}
private:
T item_;
};
int main() {
MyContainer<int> intContainer(42);
MyContainer
②実行時ポリモーフィズム:主に仮想関数(Virtual Function)と抽象クラス(Abstract Classes)によって実現されます。基本クラスの関数を仮想関数として宣言することで、派生クラスはこの関数をオーバーライド(オーバーライド)して、基本クラスとは異なる動作を実現できます。実行時に、コンパイラは、オブジェクトの実際の型 (実行時ポリモーフィズム) に基づいて、どのバージョンの仮想関数を呼び出すかを決定します。
以下は実行時ポリモーフィズムの例です。
class Animal {
public:
virtual void makeSound() {
cout << "The animal makes a sound." << endl;
}
};
class Dog : public Animal {
public:
void makeSound() override {
cout << "The dog barks." << endl;
}
};
class Cat : public Animal {
public:
void makeSound() override {
cout << "The cat meows." << endl;
}
};
この例では、Animal
仮想関数を定義する基本クラスmakeSound
。Dog
と はの派生クラスでCat
ありAnimal
、makeSound
それぞれ関数を書き換えます。これで、Animal
型のポインターまたは参照を使用しmakeSound
て関数を呼び出すことができ、実行時に、実際のオブジェクト型に従って対応する関数バージョンが呼び出されます。
Animal* animal1 = new Dog();
Animal* animal2 = new Cat();
animal1->makeSound(); // 输出 "The dog barks."
animal2->makeSound(); // 输出 "The cat meows."
これは実行時ポリモーフィズムの典型的な応用例であり、これにより、統合された柔軟な方法でさまざまなタイプのオブジェクトを処理できるようになります。