1.ポリモーフィズムの基本概念
ポリモーフィズムは、C ++オブジェクト指向の3つの主要な特性の1つです。
ポリモーフィズムは2つのカテゴリーに分けられます
- 静的ポリモーフィズム:関数のオーバーロードと演算子のオーバーロードは静的ポリモーフィズムに属し、関数名は再利用されます
- 動的ポリモーフィズム:ランタイムポリモーフィズムを実現するための派生クラスと仮想関数
静的ポリモーフィズムと動的ポリモーフィズムの違い:
- 静的ポリモーフィック関数アドレスの早期バインディング-関数アドレスはコンパイル中に決定されます
- 動的ポリモーフィック関数アドレスの遅延バインディング-関数アドレスは実行時に決定されます
ケースを通してポリモーフィズムを説明しましょう
class Animal
{
public:
//Speak函数就是虚函数
//函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。
virtual void speak()
{
cout << "动物在说话" << endl;
}
};
class Cat :public Animal
{
public:
void speak()
{
cout << "小猫在说话" << endl;
}
};
class Dog :public Animal
{
public:
void speak()
{
cout << "小狗在说话" << endl;
}
};
//我们希望传入什么对象,那么就调用什么对象的函数
//如果函数地址在编译阶段就能确定,那么静态联编
//如果函数地址在运行阶段才能确定,就是动态联编
void DoSpeak(Animal & animal)
{
animal.speak();
}
//
//多态满足条件:
//1、有继承关系
//2、子类重写父类中的虚函数
//多态使用:
//父类指针或引用指向子类对象
void test01()
{
Cat cat;
DoSpeak(cat);
Dog dog;
DoSpeak(dog);
}
int main() {
test01();
system("pause");
return 0;
}
2つの純粋仮想関数と抽象クラス
ポリモーフィズムでは、通常、親クラスでの仮想関数の実装は無意味であり、主な内容はサブクラスを呼び出して書き換えることです。
したがって、仮想関数を純粋仮想関数に変更できます。
純粋仮想関数の構文:virtual 返回值类型 函数名 (参数列表)= 0 ;
クラスに純粋仮想関数がある場合、このクラスは次のようにも呼ばれます。抽象クラス
抽象的特徴:
- オブジェクトをインスタンス化できません
- サブクラスは、抽象クラスの純粋仮想関数を書き換える必要があります。そうでない場合は、抽象クラスにも属します。
class Base
{
public:
//纯虚函数
//类中只要有一个纯虚函数就称为抽象类
//抽象类无法实例化对象
//子类必须重写父类中的纯虚函数,否则也属于抽象类
virtual void func() = 0;
};
class Son :public Base
{
public:
virtual void func()
{
cout << "func调用" << endl;
};
};
void test01()
{
Base * base = NULL;
//base = new Base; // 错误,抽象类无法实例化对象
base = new Son;
base->func();//此时调用的是子类Son的func函数
delete base;//因为是在堆区(使用了new)记得销毁
}
int main() {
test01();
system("pause");
return 0;
}
3つの架空の破壊と純粋な架空の破壊
ポリモーフィズムを使用する場合、ヒープ領域に開かれているプロパティがサブクラスにある場合、親クラスポインターは、解放されたときにサブクラスのデストラクタコードを呼び出すことができません。
解決策:親クラスのデストラクタを仮想破壊または純粋な仮想破壊に変更します
架空の分析と純粋な架空の分析の共通点:
- 親クラスポインタを解決し、サブクラスオブジェクトを解放できます
- 特定の機能を実装する必要があります
架空の破壊と純粋な架空の破壊の違い:
- 純粋な架空の破壊である場合、クラスは抽象クラスに属し、オブジェクトをインスタンス化できません
架空の分析文法:
virtual ~类名(){}
純粋な架空の分析構文:
virtual ~类名() = 0;
类名::~类名(){}
class Animal {
public:
Animal()
{
cout << "Animal 构造函数调用!" << endl;
}
virtual void Speak() = 0;
//析构函数加上virtual关键字,变成虚析构函数
//virtual ~Animal()
//{
// cout << "Animal虚析构函数调用!" << endl;
//}
//纯虚析构函数和纯虚函数不一样。前者必须在外部进行实现,不能为空
virtual ~Animal() = 0;
};
Animal::~Animal()
{
cout << "Animal 纯虚析构函数调用!" << endl;
}
//和包含普通纯虚函数的类一样,包含了纯虚析构函数的类也是一个抽象类。不能够被实例化。
class Cat : public Animal {
public:
Cat(string name)
{
cout << "Cat构造函数调用!" << endl;
m_Name = new string(name);
}
virtual void Speak()
{
cout << *m_Name << "小猫在说话!" << endl;
}
//只需要在父类中写(纯)虚析构函数即可,子类中不需要
~Cat()
{
cout << "Cat析构函数调用!" << endl;
if (this->m_Name != NULL) {
delete m_Name;
m_Name = NULL;
}
}
public:
string *m_Name;
};
void test01()
{
Animal *animal = new Cat("Tom");
animal->Speak();
//通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏
//怎么解决?给基类增加一个虚析构函数
//虚析构函数就是用来解决通过父类指针释放子类对象
//如果把这句话注释掉,那么父类、子类的析构函数都不会被调用,所以,这就说明;
//在这种父类指针指向子类对象的多态结构中,子类对象其实是一直依赖着父类指针,
//自己其实并没有单独生成,所以也不会自己在函数执行完调用析构;
//而父类因为是在堆区,所以也不会自动释放
delete animal;
}
int main() {
test01();
system("pause");
return 0;
}