C++继承与多态详解

C++继承与多态
一、继承与多态的介绍
继承可以通过使用现有的类或接口扩展所需的功能,从而实现代码的复用。

      多态可以使不同的对象调用相同的函数名运行出不同的结果。

二、继承
(1)派生类继承基类除构造函数和析构函数以外的成员变量和函数;
(2)私有成员变量可以继承到子类中,但不能访问。
(3)基类成员的构造只能通过基类构造方法构造。

(4)三种继承方式下,派生类的访问限定:


(5)继承结构中,函数之间的关系:


(6)三类函数不能成为虚函数:构造函数, static成员函数,inline函数。

举例:

#include<iostream>
using namespace std;

class Base
{
public:
	Base(int a):ma(a){cout<<"Base()"<<endl;}
	~Base(){cout<<"~Base()"<<endl;}
	void show(){cout<<"Base::show"<<endl;}
	void show(int i){cout<<"Base::show i"<<endl;}
protected:
	int ma;
};

class Child : public Base
{
public:
	Child(int a, int b):Base(a), mb(b){cout<<"Child()"<<endl;}
	~Child(){cout<<"~Child()"<<endl;}
	void show(){cout<<"Child::show"<<endl;}   
private:	
	int mb;
};
int main()
{
	//实例1:
	Base base(10);
	Child child(10,10);
	base = child;
	//child = base;//编译出错1

	//实例2:
	Base *pb1 = new Base(10);
	pb1->show();
	delete pb1;

	//实例3:
	Base *pb2 = new Child(10, 10);
	pb2->show();  
	delete pb2;

	//实例4:
	//Child *pd1 = new Base(10);//编译出错2
	//pd1->show();
	//delete pd1;

	//实例5:
	Child *pd1 = new Child(10,10);
	pd1->show();
	delete pd1;

	return 0;
}
输出:



由此例可知:
   (1)Base类中void show()与void show(int i)构成重载;
   (2)Base中void show()与Child中void show()构成隐藏;
   (3)可以将派生类对象赋给基类,但不能把基类对象赋给派生类,编译器只支持向上转换;见实例1
   (4)基类指针或引用可以指向派生类,但只能访问到派生类从基类继承而来的成员;如需访问,需要强转;见实例3

三、多态

       多态简单概括为“一个接口,多种方法”,即程序在运行时才确定调用哪个地址。C++多态是通过虚函数virtual实现的。

举例:

#include<iostream>
using namespace std;

class Base
{
public:
	Base(int a):ma(a){cout<<"Base()"<<endl;}
	~Base(){cout<<"~Base()"<<endl;}
	virtual void show(){cout<<"Base::show"<<endl;}//虚函数
	void show(int i){cout<<"Base::show i"<<endl;}
protected:
	int ma;
};

class Child : public Base
{
public:
	Child(int a, int b):Base(a), mb(b){cout<<"Child()"<<endl;}
	~Child(){cout<<"~Child()"<<endl;}
	void show(){cout<<"Child::show"<<endl;}   
private:	
	int mb;
};
int main()
{
	Base *pb2 = new Child(10, 10);
	pb2->show();  
	delete pb2;

	return 0;
}
输出:



这回编译器调用派生类的方法。

四、多态实现机制

       根据上面例子,计算sizeof(Base) = 8;基类大小是由基类成员变量决定的,即ma,占4字节,那么剩余4字节存储什么?其实在具有虚函数的基类里,还存储了虚函数指针vfptr,指向虚函数表vftable,虚函数表存放虚函数的地址。

       基类Base的存储结构如下:


        虚函数表-2行存储RTTI(RunTime Type Informatica)指针,指向运行时对象的类型信息;虚函数-1行存储虚函数指针起始地址的偏移量,一般为0,;虚函数第0行村相互类中所有的虚函数地址。

       派生类从基类继承了虚函数指针和成员变量,而且还提供了覆盖方法。派生类对象构造时,先构造基类,基类的结构如上图所示,接着构造派生类,调用基类构造方法,同时使虚函数指针指向自身的虚函数表。当函数运行时,由于虚函数指针指向派生类中的覆盖方法,则会执行派生类的覆盖函数。因而每个从基类继承而来的派生类提供了基类虚函数的覆盖方法,当使用基类指针或引用指向该派生类是,调用同名函数,从而得到不同的运行结果。


五、纯虚函数

       纯虚函数是在基类中声明的虚函数,所以继承基类的派生类需要重写实现方法。具有传虚函数的类为抽象类,不能定义对象,但可定义指针和引用。

举例:

#include<iostream>
using namespace std;

class Animal
{
public:
	Animal(string name):_name(name){}
	virtual void bark() = 0;  // 纯虚函数
protected:
	string _name;
};

class Dog : public Animal
{
public:

	Dog(string name):Animal(name){}
	void bark()
	{
		cout<<" wang wang!"<<endl;
	}
};

class Cat : public Animal
{
public:

	Cat(string name):Animal(name){}
	void bark()
	{
		cout<<" miao miao!"<<endl;
	}
};
int main()
{
	Animal *p1 = new Dog("dog");
	Animal *p2 = new Cat("cat");
	p1->bark();//输出:wang wang!
	p2->bark();//输出:miao miao!

	return 0;
}

纯虚函数是为了实现多态特性,基类不去实现,而由派生类实现。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值