c++中的多态

c++中的多态
什么是多态

多态是指有多种形态,具体来说就是一种行为有多种不同的状态。当有不同的对象完成同一个行为时,会产生不同的结果。在c++的调用同一个基类函数,会根据不同的对象的类型来调用不同的成员函数。多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。

多态的构成条件

在c++中要构成多态,必须要满足两个条件:

  • 调用函数的对象必须是引用或指针
  • 被调用的函数必须是虚函数,且完成了虚函数的重写。

多态是基于继承实现的,在继承关系下,不同类的对象,通过调用同一个基类的接口函数,可以根据不同的对象调用到不同的成员函数。

要构成多态则在调用时,调用的函数由对象来决定,而不根据调用的对象的类型来决定。

简单的多态的例子

#include <iostream>
using namespace std;

class Father{
public:
	 virtual void Eat(){
		cout << "吃两碗饭" << endl;
	}

protected:
	int count;
};

class Son : public Father{
public:
	virtual void Eat(){
		cout << "吃一碗饭" << endl;
}
private:
	int count;
};


int main()
{

	Father* father1;

	Father father;
	Son son;

// 此时的调用是根据对象来决定的,而不是根据类型来决定的
//根据对象来决定,不同的对象调用会产生不同的结果
	father1 = &father;
	father1->Eat();  //吃两碗饭
	father1 = &son;
	father1->Eat(); //吃一碗饭

//根据类型来决定
    father2 = father;
	father2.Eat();   //吃两碗饭
	father2 = son;
	father2.Eat();    //吃两碗饭


	return 0;
}

重写

虚函数:在函数前加virtual即为虚函数。

虚函数的重写: 在继承关系中派生类中有一个和基类完全相同的虚函数,即函数名, 参数类型, 参数个数,返回值(协变时可以不相同)都相同。此时称该虚函数重写了基类中的虚函数,重写也叫覆盖。此时在派生类中只能访问到派生类中的该函数,从基类中继承的该函数以经被派生类中的该函数覆盖。

协变:在重写的过程中有一个例外,其返回值可以不同,此时也构成重写。但此时返回值必须是派生类或基类指针,派生类或基类的引用。

不规范的重写行为:当派生类对基类的虚函数进行重写时,可以不用加virtual ,因为从基类继承下来的而虚函数,其默认的属性仍为虚函数,我们只对其进行重写。但这是一种不规范的写法

派生类的析构函数与基类的析构函数构成了重写。

派生类的析构函数与基类的析构函数尽管函数名不相同,但在编译器的特殊处理下,两个析构函数被处理成了同名函数destructor。所以要将基类的析构函数做好写成虚函数。

为什么将析构函数写成虚函数

如果不使用在析构函数的时候不使用virtual,使用动态绑定,则在析构的时候就会忽略掉派生类的部分,如果我们派生类中进行了空间的开辟,而在派生类的析构中对其进行释放,如过不调用派生类析构,会造成内存泄漏。

class Father {
public:
	virtual ~Father() { cout << "~Father()" << endl; }
};
class Son : public Father {
public:
	 ~Son() { cout << "~Son()" << endl; }
};

//只有基类析构函数与派生类的析构函数构成多态时,在析构时才能正确的析构不同对象
int main()
{
   
	Father* father1 = new Father;
	Father* father2 = new Son;
	delete father1;
	delete father2;
	// 结果 ~Father()
	//        ~Son()
	//        ~Father
	return 0;
}

//当基类的析构函数不是虚函数时,两个析构函数没有构成多态,
//所以在析构的时候,根据类型析构,忽略了派生类的部分,会造成内存泄漏

class Father {
public:
	 ~Father() { cout << "~Father()" << endl; }
};
class Son : public Father {
public:
	 ~Son() { cout << "~Son()" << endl; }
};

int main()
{
	Father* father1 = new Father;
	Father* father2 = new Son;
	delete father1;
	delete father2;
/* 	结果: ~Father()
           ~Father()
           //可以看到只对基类不部分进行了析构,而并没有对派生类部分进行析构
	   */
	return 0;
}
关于重载 隐藏 重写三者之间的关系

重载:在同一个作用域中即同一个类中,函数名相同,参数个数或类型不同就构成了重载。

隐藏:在两个不同的作用域中,即在继承关系中的基类和派生类两个作用域中,如果派生类与基类中有同名函数,如果不是重写就一定构成了隐藏。隐藏也叫重定义。

重写:在两个不同的作用域中,即派生类与基类中如果函数名,参数,返回值(协变除外)都相同,就构成了重写。两个函数都必须是虚函数。重写称为覆盖。

实现继承与接口继承
  • 实现继承:即为一般的继承,派生类继承基类的函数进行使用。
  • 接口继承:派生类继承基类的函数后对其进行重写,相当于仅继承了其接口。

抽象类

纯虚函数:

纯虚函数即值在函数后面添加 = 0 ,只对函数进行声明,并不对函数进行实现。而在派生类中对于函数进行实现。

  • 包含纯虚函数即的类为抽象类
  • 抽象类不能实例化出对象
  • 如果继承了抽象类的派生类不重写纯虚函数,该派生类仍为抽象类,也不能实例化对象
  • 当派生类中将继承的抽象类的纯虚函数都重写实现了,才可以实例化出对象。
  • 抽象类的纯虚函数必须被重写,可以在派生类或派生类的派生类,可以一直被继承下去在,直到被重写实现为止。
  • 纯虚函数相当于接口,体现出了接口继承
#include <iostream>
using namespace std;

class Person{
public:
	virtual void Drink() = 0;
};

class Children : public Person{
public:
	virtual void Drink() {
		cout << "drinking juice " << endl;
	}
};

class Parents : public Person{
public:
	virtual void Drink() {
		cout << "drinking wine" << endl;
	}
};

int main()
{
	Person* per = new Person;  //会报错,无法实例化对象
	Children*  chil = new Children;
	return 0;
c++11 中的两个关键字

final

  • 被final修饰的类不能被继承
  • 被final修饰的函数不能被重写
  • 被final修饰的变量不能被修改

override
在派生类中显示的声明,那些函数需要重写,如果没有重写,编译器就会报错。

class Parents : public Person{
public:
	virtual void Drink() override {
		cout << "drinking wine" << endl;
	}
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值