c++面向对象中的多态全解

虚函数

定义:前面有virtual关键字的成员函数就是虚函数, virtual 关键字只用在类定义里的函数声明里,写函数体时不需要

class base{
	virtul int get();
};
int base::get(){}

多态的表现形式一

  1. 派生类的指针可以赋值给基类指针
  2. 通过基类指针调用基类和派生类中的同名虚函数时
    1. 若指针指向一个基类对象,那么被调用的是基类的虚函数
    2. 若指针指向一个派生类对象,那么被调用的是派生类的虚函数
      示例
class CBase{
	public:
		virtual void SomeVirtualFunction() { }
};
class CDerived:public Cbase{
	public:
		virtual void SomeVirtualFunction() { }
};
int main(){
	CDerived ODerived;
	CBase *p = & ODerived;
	p->SomeVirtualFunction();
	return 0;
}

多态的表现形式二

  1. 派生类的对象可以赋给基类的引用
  2. 通过基类引用可以调用基类和派生类中的同名虚函数时:
    1. 该引用引用的是一个基类对象,那么调用的是基类的虚函数
    2. 若该引用引用的是一个派生类的对象, 那么被调用的是派生类的虚函数
class CBase{
	public:
		virtual void SomeVitualFunction(){ }
};
class CDerived:public CBase{
	public:
		virtual void SomeVirtualFunction() { }
};
int main(){
	CDerived ODerived;
	CBase &r = ODerived;
	r.SomeVirtualFunction();
	return 0;
}
另外一个引例
class A { 
	public : 
	virtual void Print( ) 
	{ cout << "A::Print"<<endl ; }
};
class B: public A { 
	public : 
	virtual void Print( ) { cout << "B::Print" <<endl; }
};
class D: public A {
	public : 
	virtual void Print( ) { cout << "D::Print" << endl ; }
};
class E: public B {
	public : 
	virtual void Print( ) { cout << "E::Print" << endl ; }
};
int main() {
	A a; B b; E e; D d;
	A * pa = &a; B * pb = &b; 
	D * pd = &d ; E * pe = &e; 
	pa->Print();    // 输出A::Print
	pa = pb; 
	pa -> Print();  // 输出B::Print
	pa = pd;
	pa -> Print();  // 输出D::Print
	pa = pe;
	pa -> Print();  // 输出E::Print
	return 0;
}
一个示例

几何形体处理
按面积从小到大依次输出每个几何形体的总类及面积,每个几何形体的输出格式为: 形体名称:面积

#inlcude<iostream>
#include<stdlib.h>
#include<math.h>
using namespace std;
class CShape{
	public:
		virtual double Area() = 0;
		virtual void PrinInfo() = 0;
};
class CRectangle:public CShape{
	public:
		int w, h;
		virtual double Area();
		virtual void PrintInfo();
};
class CCircle:public CShape{
	public:
		int r;
		virtual double Area();
		virtual void PrintInfo();
};
class CTriangle: public CShape{
	public:
		int a, b, c;
		virtual double Area();
		virtual void Printlnfo();
};
double CTriangle::Area(){
	return w*h;
}
void CRectangle::Printfo(){
	cout << "Rectangle:" << Area << endl;
}
double CCircle::Area(){
	return 3.14*r*r;
}
void CCircle::PrintInfo(){
	cout << "CCircle:" << Area() << endl; 
}
double CTriangle::Area(){
	double p = (a+b+c)/2.0;
	return sqrt(p*(p-a)*(p-b)*(p-c));
}
void CTriangle::PrintInfo(){
	cout << "Traiangle:" << Area() << endl; 
}
CShape * pShapes[100];
int MyCompare(const void *s1, const void *s2);
int main(){
	int i;int n;
	CRectangle *pr;
	CCircle *pc;
	CTriangle *pt;
	cin >> n;
	for(int i=0; i<n;i++){
		char c;
		cin >> c;
		switch(c){
			case "R":
				pr = new CRectangle();
				cin >> pr->w >> pr->h;
				pShapes[i] = pr;
				break;
			case "C":
				pc = new CCircle();
				cin >> pc-> r;
				pShapeds[i] = pc;
				break;
			case 'T':
				pt = new CTriangle();
				cin >> pt->a >> pt->b >> pt->c;
				pShapes[i] = pt;
				break;
		}
	}
	qsort(pShapes, n, sizeof(CShape *), MyCompare);
	for(int i=0;i,n;i++){
		pShapes[i]->PrintInfo();
	}
	return 0;
}
int MyCompare(const void *s1, const void *s2){
	double a1, a2;
	CShape **p1;
	CShape **p2;
	p1 = (CShape**) p1;
	p2 = (CShape**) p2;
	a1 = (*p1)->Area();
	a2 = (*p2)->Area();
	if(a1 < a2) return -1;
	else if (a2 < a1) return 1;
	else return 0;
}

用基类指针数组存放指向各种派生类对象的指针,然后遍历该数组,就能对各个派生类对象做各种操作,是很常用的做法

class Base{
	public:
		void fun1() { this -> fun2(); }
		virtual void fun2() { cout << "Base::fun2()" << endl; }
};
class Derived:public Base{
	virtual void fun2() { cout << "Derived:fun2() " << endl;}
};
int main(){
	Derived d;
	Base *pBase = &d;
	pBase->fun1();
	return 0;
}
// 输出: Derived:fun2()
// 在非构造函数中,非构造函数的成员函数调用虚函数,是多态!!!!!

在构造函数和析构函数中调用虚函数,不是多态,编译时可以确定,调用的函数是自己类或者基类中定义的函数,不会等到运行时才决定调用自己的还是派生类的函数

class myclass{
	public:
		virtual void hello(){ cout << "hello from myclass" << endl; };
		virtual void bye() { cout << "bye from myclas " << endl;}
};
class son:public myclass{
// 派生类中和基类中虚函数同名同参数表的函数,不加virtual也自动成为虚函数
	void hello() { cout << "hello from son " << endl; };
	son() { hello(); };
	~son() { bye(); };
};
class grandson: public son {
	public:
		void hello(){cout<<"hello from grandson"<<endl;};
		void bye() { cout << "bye from grandson"<<endl;}
		grandson(){cout<<"constructing grandson"<<endl;};
		~grandson(){cout<<"destructing grandson"<<endl;};
};
int main(){
	grandson gson;
	son *pson;
	pson=&gson; 
	pson->hello(); //多态
	return 0;
}
//输出
//hello from son
//constructing grandson
//hello from grandson
//destructing grandson
//bye from myclass

虚函数的访问权限

class Base {
	private:
	virtual void fun2() { cout << "Base::fun2()" << endl; }
};
class Derived:public Base {
	public: 
	virtual void fun2() { cout << "Derived:fun2()" << endl; }
};
Derived d;
Base * pBase = & d;
pBase -> fun2(); // 编译出错

原因:编译出错的原因是fun2()是Base的私有成员,运行时不考虑调用的是哪个, 如果 将Base中的 private换成public,即使Derived中的fun2() 是private的,编译依然能通过,也能正确调用Derived::fun2()。

多态实现的原理
每一个有虚函数的类(或有虚函数的类的派生类)都有一个虚函数表,该类的任何对象中都放着虚函数表的指针。虚函数表中列出了该类的虚函数地址。

#include <iostream>
using namespace std;
class A {
	public: virtual void Func() { cout << "A::Func" << endl; } 
};
class B:public A {
	public: virtual void Func() { cout << "B::Func" << endl; } 
};
int main() {
	A a;
	A * pa = new B();
	pa->Func();
	//64位程序指针为8字节
	long long * p1 = (long long * ) & a; 
	long long * p2 = (long long * ) pa;
	* p2 = * p1; 
	pa->Func();
	return 0; 
}
// 输出
// B:Func
// A:Func
虚析构函数
  1. 通过基类的指针删除派生类对象时,通常情况下只调用基类的析构函数
    但是,删除一个派生类的对象时,应该先调用派生类的析构函数,然后调用基类的析构函数。
  2. 解决办法:把基类的析构函数声明为virtual
    派生类的析构函数可以virtual不进行声明
    通过基类的指针删除派生类对象时,首先调用派生类的析构函数,然后调用基类的析构函数
  3. 一般来说,一个类如果定义了虚函数,则应该将析构函数也定义成虚函数。或者,一个类打算作为基类使用,也应该将析构函数定义成虚函数。
  4. 不允许以虚函数作为构造函数
class son{
	public:
		~son() {cout<<"bye from son"<<endl;};
};
class grandson:public son{
	public:
		~grandson(){cout<<"bye from grandson"<<endl;};
};
int main(){
	son *pson;
	pson=new grandson();
	delete pson;
	return 0;
}
// 输出: bye from son 没有执行grandson::~grandson()!!!
class son{
	public:
		virtual ~son() {cout<<"bye from son"<<endl;};
};
class grandson:public son{
	public:
		~grandson(){cout<<"bye from grandson"<<endl;};
};
int main() {
	son *pson;
	pson= new grandson();
	delete pson;
	return 0;
}
// 输出
//bye from grandson
//bye from son
//执行grandson::~grandson(),引起执行son::~son()!!!

纯虚函数和抽象类
纯虚函数: 没有函数体的虚函数

class A {
	private: int a;
	public:
		virtual void Print( ) = 0 ; //纯虚函数
		void fun() { cout << "fun"; } 
};
  1. 包含纯虚函数的类叫抽象类
    1. 抽象类只能作为基类来派生新类使用,不能创建抽象类的对象
    2. 抽象类的指针和引用可以指向由抽象类派生出来的类的对象
  2. 在抽象类的成员函数内可以调用纯虚函数,但是在构造函数或析构函数内不能调用纯虚函数。
  3. 如果一个类从抽象类派生而来,那么当且仅当它实现了基类中的所有纯虚函
    数,它才能成为非抽象类
class A {
	public:
	virtual void f() = 0; //纯虚函数
	void g( ) { this->f( ) ; //ok
}
A( ){ //f( ); // 错误
}
};
class B:public A{
	public:
	void f(){ cout<<"B:f()"<<endl; }
};
int main(){
	B b;
	b.g();
	return 0;
}
// 输出B:f()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值