继承可以通过使用现有的类或接口扩展所需的功能,从而实现代码的复用。
多态可以使不同的对象调用相同的函数名运行出不同的结果。
二、继承(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;
}
纯虚函数是为了实现多态特性,基类不去实现,而由派生类实现。