C++ 多态

多态简介

多态(Polymorphism)是一种允许不同类的对象对同一消息做出响应的能力,这个响应的确切行为取决于对象的实际类型。多态是面向对象编程的核心概念之一,它使得代码更加灵活和可扩展。C++中的多态主要有两种形式:编译时多态(也称为静态多态),编译阶段确定函数的地址;运行时多态(也称为动态多态),在运行阶段确定函数的地址。

静态多态(Static Polymorphism)

  1. 函数重载(Overloading)

    • 允许在同一作用域内声明多个同名函数,只要它们的参数列表不同。
  2. 运算符重载(Operator Overloading)

    • 允许为自定义类型重新定义运算符的行为。
  3. 模板(Templates)

    • 允许创建泛型类和函数,它们可以在编译时根据给定的类型参数实例化。

动态多态(Dynamic Polymorphism)

  1. 虚函数(Virtual Functions)

    • 当一个基类的成员函数被声明为虚函数时,派生类可以覆盖(Override)它。运行时,通过基类的指针或引用调用虚函数时,将调用对象实际类型的版本。
  2. 动态绑定(Dynamic Binding)

    • 与虚函数一起工作,确保调用正确的函数版本,即使基类指针或引用指向的是派生类对象。
  3. 抽象类(Abstract Classes)

    • 不能实例化的类,通常包含至少一个纯虚函数。它们用作接口或基类。

基本语法

实现动态多态:父类的引用指向子类的对象,实现地址晚绑定。

1.有继承

2.子类有重写父类的虚函数,与重载不同,重写完全一样,类型,名称,参数都一样

测试案例:

运行测试代码,发现传入的是cat,(传入的是父类的指针,指向子类)执行的却是父类中的函数。因为地址早绑定了父类的地址,若想执行子类中的执行函数,则需要在父类函数前加virtual即可,这样操作即可实现地址晚绑定。  

//建立一个动物了类
class Animal
{
public:
	void say()
	{
		cout << "动物在叫唤" << endl;
	}
};

class Cat :public Animal
{
public:
	void say()
	{
		cout << "猫在叫唤" << endl;
	}
};

void dosay(Animal &animal)
{
	animal.say();
}

int main()
{
	//
	// test();
	Animal animal;
	Cat cat;
	dosay(cat);

	system("pause");
	return 0;
}

本质分析

int main()
{
	class Animal
	{
	public:
		void say()
		{
			cout << "动物在叫唤" << endl;
		}
	};
	Animal animal;
	cout <<" sizeof(animal) ==" << sizeof(animal) << endl;;
	system("pause");
	return 0;
}

查看anmial对象的内存大小可知,所占只有一个字节,因为这个类是个空类

随后在成员函数前加上virtual关键字后打印

void virtual say()

此时说明该类变成了一个指针,指向了内部虚函数表,表类记录着虚函数的地址。当子类重写父类中的虚函数,子类中的虚函数表内部会替换成子类的虚函数地址,父函数中的虚函数表并没有发生变化。

纯虚函数和抽象类

多态中,通常父类中的虚函数实现是没有意义的,主要是调用子类重写的内容

因此,可以将虚函数改写为纯虚函数

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

当类中有了纯虚函数,这个类也称为抽象类

特点:

无法实例化对象

子类必须重写抽象类中的纯虚函数,否则也属于抽象类

代码示例:

//构建父类
class Base
{
public:
	//纯虚函数
	//只有有一个纯虚函数,此类成为抽象类
	//1.无法示例化对象
	//2.抽象类的子类必须重写父类中的纯虚函数,否则也属于抽象类,无法实例化对象
	virtual void func() = 0;
};

class Son:public Base
{
public:
	virtual void func()
	{
		cout << "func函数调用!" << endl;
	};
};
void test()
{
	//利用多态:父类指针指向子类对象
	Base* base = new Son;
	base->func();
}

虚析构与纯虚析构

解决:多态使用时,若子类中有属性开辟在堆区,那么父类指针在释放时无法调用到子类的析构代码,此时可以将父类中的析构函数改为虚析构与纯虚析构!

共性:

都可以解决父类指针释放子类的对象

都需要具体的函数实现

区别:

若为虚析构,则该类属于抽象类,无法实例化对象

虚析构:virtual ~类名(){}

纯虚析构: virtual ~类名()=0;

~类名()

{}

class Anmial
{
public:
	Anmial()
	{
		cout << "ANMIAL构造函数!" << endl;
	}
	~Anmial()
	{
		cout << "ANMIAL析构函数!" << endl;
	}
	//纯虚函数
	virtual void func() = 0;
};

class Cat:public Anmial
{
public:
	Cat(string name)
	{
		cout << "CAT构造函数调用" << endl;
		//创建在堆区返回一个指针
		m_Name=new string(name);
	}
	//子类需要重写,否则视为抽象类
	virtual void func()
	{
		cout <<*m_Name <<"小猫在说话" << endl;
	}
	~Cat()
	{
		if (m_Name != NULL)
		{
			cout << "CAT析构函数调用" << endl;
			delete m_Name;
			m_Name = NULL;
		}
	}
	string* m_Name;
};
void test()
{
	Anmial*animal = new Cat("TOM!");
	animal->func();
	delete animal;
}

运行结果:

由此可见,cat类的析构函数并没有被执行,是因为父类指针在析构的时候,不会调用子类中的析构函数,导致子类如果有堆区的属性,导致内存泄漏问题。

解决措施:利用虚析构解决父类指针释放子类对象时不干净的问题

将父类析构改为虚析构   virtual~Anmial()

纯虚析构需要函数声明和具体的函数实现

class Anmial
{
public:
	Anmial()
	{
		cout << "ANMIAL构造函数!" << endl;
	}
	virtual~Anmial() = 0;

	virtual void func() = 0;
};

Anmial::~Anmial()
{
	cout << "Anmial纯虚析构函数" << endl;
}

虚析构与纯虚析构使用来解决通过父类指针释放子类对象的

若子类中没有堆区的数据,可以不写纯虚析构或者虚析构

拥有纯虚析构的函数的类属于抽象类,无法实例化对象

C++中的多态(Polymorphism)是指在父类和子类之间的相互转换,以及在不同对象之间的相互转换。 C++中的多态性有两种:静态多态和动态多态。 1. 静态多态 静态多态是指在编译时就已经确定了函数的调用,也称为编译时多态C++中实现静态多态的方式主要有函数重载和运算符重载。 函数重载是指在同一作用域内定义多个同名函数,但它们的参数列表不同。编译器根据传递给函数的参数类型和数量来确定调用哪个函数。例如: ```c++ void print(int num) { std::cout << "This is an integer: " << num << std::endl; } void print(double num) { std::cout << "This is a double: " << num << std::endl; } int main() { int a = 10; double b = 3.14; print(a); // 调用第一个print函数 print(b); // 调用第二个print函数 } ``` 运算符重载是指对C++中的运算符进行重新定义,使其能够用于自定义的数据类型。例如: ```c++ class Complex { public: Complex(double real, double imag) : m_real(real), m_imag(imag) {} Complex operator+(const Complex& other) const { return Complex(m_real + other.m_real, m_imag + other.m_imag); } private: double m_real; double m_imag; }; int main() { Complex a(1.0, 2.0); Complex b(3.0, 4.0); Complex c = a + b; // 调用Complex类中重载的+运算符 } ``` 2. 动态多态 动态多态是指在运行时根据对象的实际类型来确定调用哪个函数,也称为运行时多态C++中实现动态多态的方式主要有虚函数和纯虚函数。 虚函数是在父类中定义的可以被子类重写的函数,使用virtual关键字声明。当一个对象的指针或引用指向一个子类对象时,调用虚函数时会根据实际的对象类型来确定调用哪个函数。例如: ```c++ class Shape { public: virtual void draw() { std::cout << "Drawing a shape." << std::endl; } }; class Circle : public Shape { public: void draw() override { std::cout << "Drawing a circle." << std::endl; } }; int main() { Shape* shape_ptr = new Circle(); shape_ptr->draw(); // 调用Circle类中重写的draw函数 } ``` 纯虚函数是在父类中定义的没有实现的虚函数,使用纯虚函数声明(如virtual void func() = 0;)。父类中包含纯虚函数的类称为抽象类,抽象类不能被实例化,只能作为基类来派生子类。子类必须实现父类的纯虚函数才能实例化。例如: ```c++ class Shape { public: virtual void draw() = 0; }; class Circle : public Shape { public: void draw() override { std::cout << "Drawing a circle." << std::endl; } }; int main() { Shape* shape_ptr = new Circle(); shape_ptr->draw(); // 调用Circle类中重写的draw函数 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值