C++_多态、重载重写重定义、纯虚函数和抽象类

多态:一种调用语句有多种表现形态

错误示例

class parent{
public:
	parent(int a){
		this->a=a;
		cout<<"parent a:"<<a<<endl; 
	}
	void print(){
		cout<<"hello"<<endl;
	}
private:
	int a;
}; 
class child: public parent{
public:
	child(int b){
		this->b=b;
		cout<<"child b:"<<b<<endl;
	}
	void print(){
		cout<<"hello"<<endl;
	}
private:
	int b;
};
int main(){
	parent p(10);
	child c(20);
	parent *base=NULL;
	
	base=&p;
	base->print();	执行parent的成员函数
	
	base=&c;
	base->print();	执行parent的成员函数
	
	parent &base=p;
	base.print();	执行parent的成员函数
	
	parent &base=c;
	base.print();	执行parent的成员函数
	
	不管是指针还是引用做函数参数,都执行parent的成员函数 
}
解决方案:用virtual关键字 
注意:如果使用了virtual关键字,但是两个类中的函数原型不一样,也不会发生多态。
函数原型指的是函数的头。

class parent{
public:
	parent(int a){
		this->a=a;
		cout<<"parent a:"<<a<<endl;
	}
	virtual void print(){	 	父类必须要写virtual 
		cout<<"hello"<<endl;
	}
private:
int a; 
}; 
class child: public parent{
public:
	child(int b){
		this->b=b;
		cout<<"child b:"<<b<<endl;
	}
	virtual void print(){		如果父类写了virtual,子类可以不写 
		cout<<"hello"<<endl;
	}
private:
	int b;
}; 
int main(){
	parent p(10);
	child c(20);
	parent *base=NULL;
	
	base=&p;
	base->print();		执行parent的成员函数
	
	base=&c;
	base->print();		执行child的成员函数
	
	parent &base=p;
	base.print();		执行parent的成员函数
	
	parent &base=c;
	base.print();		执行child的成员函数 
}

面向对象三大思想:封装、继承、多态

实现多态三个条件:
1 要有继承
2 要有函数重写virtual
3 用父类指针或引用指向子类对象

虚析构函数
构造函数不能是虚函数,建立一个派生类对象时,必须从类层次的根开始,
沿着继承路径逐个调用基类的构造函数
析构函数可以是虚的,虚析构函数用于指引delete运算符正确析构动态对象

class A{
public:
	A(){
		p=new char[20];
		strcpy(p, "abc");
	}
	~A(){							virtual~A()构成虚析构函数
		delete []p;
	}
private:
	char *p;
}; 

class B: public A{
public:
	B(){
		p=new char[20];
		strcpy(p, "def");
	}
	~B(){							virtual~B()构成虚析构函数
		delete []p;
	}
private:
	char *p;
};

class C: public B{
public:
	C(){
		p=new char[20];
		strcpy(p, "xyz");
	}
	~C(){
		delete []p;
	}
private:
	char *p; 
};
void how_to_delete(A *base){  注意形参是A类型 
	delete base;
	不会表现出多态的属性,只执行A的析构函数,需要在所有基类的析构函数前加virtual 
}
int main(){
	C *p=new C;
	delete p;	这句话的效果和执行函数的效果不同,直接通过子类对象析构不需要写virtual 
	
	how_to_delete(p);
	只会执行A的析构函数 
} 

重载、重写、重定义

重载必须在一个类的内部;子类无法重载父类的函数,父类同名函数被覆盖;
重载实在编译期间根据参数类型和个数调用。

重写发生字父类和子类的两个类之间。
重写分为两类
1 虚函数重写,使用virtual关键字,发生多态;多态是在运行期间根据对象具体的类型决定调用
2 非虚函数重写 不使用virtual关键字(叫重定义)

class parent{
public:
	void abc(){
		cout<<"abc"<<endl;
	}
	
public:
	三个函数是重载的关系 
	virtual void func(){
		cout<<"func() do"<<endl;
	}
	virtual void func(int i){
		cout<<"func(int i) do"<<endl;
	}
	virtual void func(int i, int j){
		cout<<"func(int i, int j) do"<<endl;
	}
};
class child: public parent{
public:
	两个函数时重载关系 
	其中parent和child中的(int i, int j)两个函数为重写中的虚函数重写-->多态 
	virtual void func(int i, int j){
		cout<<"func(int i, int j) do"<<endl;
	}
	virtual void func(int i, int j, int k){
		cout<<"func(int i, int j, int k) do"<<endl;
	}
	
public:
	void abc(){
		cout<<"abc"<<endl;
	}
	两个类中的abc函数为重写中的非虚函数重写(重定义),覆盖;但可用域作用符调用父类函数 
};
int main(){
	child c;
	c.func();	err:没有重载函数接受0个参数,子类含有和父类相同的函数,覆盖掉
				因为子类中已经有了func名字的函数,编译器不会在父类中找 
	c.parent::func();	ok		
}

多态原理研究
多态成立的三个条件:1、继承。2、虚函数重写virtual。3、父类指针(或引用)指向子类对象。

class parent{
public:
	parent(int a){
		this->a = a;
	}
	virtual void print(){
		cout << "parent..." << endl;
	}
private:
	int a;
};

class child : public parent
{
public:
	child(int a = 0, int b = 0) : parent(a){
		this->b = b;
	}
	virtual void print(){
		cout << "child..." << endl;
	}
private:
	int b;
};
void how_to_play(parent *base){
	base->print();
	效果:传来父类对象,执行父类对象的函数;传来子类对象,执行子类对象的函数 
	父类对象和子类对象分别与vptr指针-->虚函数表-->函数的入口地址 
}
int main(){
	parent t;
	child c;
	how_to_play(&p);
	how_to_play(&c);	多态发生 
}

多态的实现原理

当类中声明虚函数时,编译器会在类中生成一个虚函数表
虚函数表是一个存储类成员函数指针的数据结构
虚函数表由编译器自动生成和维护
virtual成员函数会被编译器放在虚函数表中
存在虚函数时,每个对象都有一个直线跟虚函数表的指针(vptr指针)

加入virtual函数的类定义的对象,会多一个vptr指针,其所占内存比没有
virtual的多4个字节
 
对象在创建时,有编译器对vptr指针进行初始化,只有当对象的构造函数完全结束时,vptr指针才最终确定
父类对象的vptr指针只能指向父类虚函数表,子类的只能指向子类虚函数表

class parent{
public:
	parent(int a){
		this->a=a;
	}
	virtual void print(){
		cout<<"hello"<<endl;
	}
}; 
class child: public parent{
public:
	child(int b){
		this->b=b;
	}
	virtual void print(){
		cout<<"hello"<<endl;
	}
};
int main(){
	child c;
	初始化c的vptr指针,初始化是分步的
	当执行父类的构造函数时,把c的vptr指针指向父类的虚函数表
	当执行子类的构造函数时,把c的vptr指针指向子类的虚函数表 
}

纯虚函数和抽象类

纯虚函数是在基类中说明的虚函数,在基类中没有定义,要求任何派生类都定义自己的版本
纯虚函数为派生类提供了一个公共的界面
纯虚函数说明形式:virtual 类型 函数名(参数表)=0
一个具有纯虚函数的基类为抽象类


#include <iostream>
using namespace std;

class parent{
public:
	virtual void get_area()=0;	纯虚函数 
};
class circle: public parent{
public:
	circle(int r){
		this->r=r;
	}
	virtual void get_area(){
		cout<<"圆面积"<<3.14*r*r<<endl;
	} 
private:
	int r;
}; 
class tringle: public parent{
public:
	tringle(int a, int h){
		this->a=a;
		this->h=h; 
	}
	virtual void get_area(){
		cout<<"三角形面积"<<a*h/2<<endl;
	}
private:
	int a;
	int h;
};
void play(parent *base){
	base->get_area();
}
int main(){
	circle c(10);
	tringle t(3, 4);
	play(&c);
	play(&t);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

H4ppyD0g

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值