c++学习第九讲---类和对象---多态

多态:

1.多态的基本概念:

(1)多态分为两类:

静态多态:函数重载和运算符重载;
动态多态:派生类和虚函数实现运行时多态。

(2)静态多态和动态多态的区别:

静态多态的函数地址在编译阶段确定(早);
动态多态的函数地址在运行阶段绑定(晚)。

(3)动态多态满足条件:

1.有继承关系;
2.子类重写父类中的虚函数;
3.父类的指针或引用指向子类的对象。

例:

class Animal
{
public:
	void speak()
	{
		cout << "动物说话" << endl;
	}
};
class Cat :public Animal
{
public:
	void speak()
	{
		cout << "猫说话" << endl;
	}
};

//地址在编译阶段就确定地址了
void doSpeak(Animal& animal)
{
	animal.speak();
}

int main()
{
	Cat cat;
	doSpeak(cat);//父类的引用接受子类的对象,结果为“动物说话”
	return 0;
}

上面的代码,即使传猫的对象,结果也为“动物说话”,是因为函数的地址在编译阶段就确定了,一定会执行animal类里的speak;

如想执行“猫说话”,则函数地址需要在运行阶段绑定,需在 animal 中的 speak 函数前加 virtual。


class Animal
{
public:
	virtual  void speak()
	{
		cout << "动物说话" << endl;
	}
};

原理:

 Animal 类内部记录了一个虚函数指针,指向一个虚函数表,表中记录了Animal::speak函数地址。

Cat 类内部继承了同样的虚函数指针,当子类重写虚函数,会将表中的父类函数指针覆盖。

c++学习第九讲---类和对象---多态_第1张图片

2.纯虚函数和抽象类:

在上面的案例中,父类中的虚函数基本没有使用的必要,因此函数内部可以不写内容,将虚函数变为纯虚函数。

纯虚函数写法:virtual 返回值类型 函数名 (参数列表)= 0 ;

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

抽象类特点:

(1)无法实例化对象
(2)子类必须重写抽象类中的纯虚函数,否则也属于抽象类。

例:

定义一个抽象类

class Base
{
public:
	virtual void func() = 0;
};
int main()
{
	Base b;//报错,纯虚函数无法实例化对象
}

子类要重写纯虚函数

class Son :public Base
{
public:
	virtual void func()
	{
		cout << "子类重写纯虚函数" << endl;
	}  
};

3.虚析构和纯虚析构:

问题:多态使用时子类中如果有属性开辟到堆区,那么父类指针在释放时无法调用子类的析构函数,可能会造成内存泄漏。

解决方法:虚析构

例:如果不用虚析构:

class Base
{
public:
	virtual void func() = 0;
	Base()
	{
		cout << "Base构造函数调用" << endl;
	}
	~Base()
	{
		cout << "Base析构函数调用" << endl;
	}
};
class Son :public Base
{
public:
	Son(string name)
	{
		m_name = new string(name);
		cout << "Son构造函数调用" << endl;
	}
	~Son()
	{
		if (m_name != NULL)
		{
			cout << "Son析构函数调用" << endl;
			delete m_name;
			m_name = NULL;
		}
	}
	virtual void func()
	{
		cout << m_name << endl;
	}  
	string* m_name;
};
int main()
{
	Base* base=new Son ("张三");
	base->func();
	delete base;
}

结果为:c++学习第九讲---类和对象---多态_第2张图片

并没有进入Son的析构函数,因此内存泄漏了。

解决:

(1)虚析构:直接在析构函数前加virtual

	virtual~Base()
	{
		cout << "Base析构函数调用" << endl;
	}

(2)纯虚析构:在类内声明,类外实现

class Base
{
public:
	virtual void func() = 0;
	Base()
	{
		cout << "Base构造函数调用" << endl;
	}
	virtual~Base() = 0;
};
Base::~Base()
{
	cout << "Base纯虚析构函数调用" << endl;
}

总结:

1.虚析构或纯虚析构就是用来解决通过父类指针释放子类对象。
2.如果子类没有在堆区开辟内存,可以不写虚析构。
3.拥有纯虚析构的类也属于抽象类。

你可能感兴趣的:(学习,c++)