C++ study notes fourteen: polymorphism of the three major characteristics of object-oriented

1. The basic concept of polymorphism

Polymorphism is one of the three major characteristics of C++ object-oriented

Polymorphism is divided into two categories

  • Static polymorphism: Function overloading and operator overloading belong to static polymorphism, and function names are reused
  • Dynamic polymorphism: derived classes and virtual functions to achieve runtime polymorphism

The difference between static polymorphism and dynamic polymorphism:

  • Early binding of static polymorphic function addresses-function addresses are determined during compilation
  • Dynamic polymorphic function address late binding-the function address is determined at runtime

Let's explain polymorphism through a case

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;
}

Two, pure virtual functions and abstract classes

In polymorphism, usually the implementation of virtual functions in the parent class is meaningless, and it is mainly the content that is rewritten by calling the subclass

Therefore, the virtual function can be changed to a pure virtual function

Pure virtual function syntax:virtual 返回值类型 函数名 (参数列表)= 0 ;

When there are pure virtual functions in the class, this class is also calledAbstract class

Abstract features :

  • Cannot instantiate object
  • The subclass must rewrite the pure virtual function in the abstract class, otherwise it also belongs to the abstract class
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;
}

Three, fictitious destruction and pure fictitious destruction

When using polymorphism, if there are properties in the subclass that are opened to the heap area, then the parent class pointer cannot be called to the subclass's destructor code when it is released

Solution: Change the destructor in the parent class to virtual destruction or pure virtual destruction

The commonality between fictitious analysis and pure fictitious analysis:

  • Can solve the parent class pointer and release the subclass object
  • Need to have specific function implementation

The difference between fictitious destruction and pure fictitious destruction:

  • If it is pure fictitious destruction, the class belongs to an abstract class and the object cannot be instantiated

Fictitious analysis grammar:

virtual ~类名(){}

Pure fictitious analysis syntax:

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;
}

Guess you like

Origin blog.csdn.net/qq_39507748/article/details/109567951