基类
class A{
public:
void printA(){cout<<"Class A";}
};
子类
class B{
public:
void printA(){cout<<"Class AB";}
void printB(){cout<<"Class B";}
};
A* p_a = new A;
((B*)p_a)->printA(); ///输出"Class AB",调用的是子类的函数,但是危险
A* p_ab = new B;
p_ab->printA(); ///输出“Class A", 调用的是基类的函数
///p_ab->printB(); error,printB不是A的成员
///(B*)p_ab->printB(); error,printB不是A的成员
((B*)p_ab)->printA(); ///输出"Class AB",调用的是子类的函数
///(B*)p_ab->printA(); error, 无法由void*转换为B*,运算符优先级问题,这里会被解释为试图将printA的返回值转换为(B*)
///B* p_b = new A; error,无法从A*转换为B*
B* p_b = new B;
p_b->printA(); ///输出“Class AB",若B没有实现printA,将会调用A的printA输出”Class A"
p_b->printB();
///B* p_ba = p_a; error,无法从A*转换为B*
B* p_ba = (B*)p_a; ///可以通过强转将子类指针指向父类
p_ba->printA(); ///输出"Class AB",调用子类的函数
p_ba->printB(); ///可以调用,但是危险
总结:
1.基类的指针指可以向派生类的对象,但指向的是派生类中基类的部分。所以只能操作派生类中从基类中继承过来的数据和基类自身的数据
2.基类指针经过强转后可以调用派生类的成员函数
3.派生类的指针,因为内存空间比基类长,所以原则上不允许派生类的指针指向基类,直接赋值会编译出错,但是可以强转,不过这样的操作很危险
4.无论父类里面有没有虚函数,都可以定义指向子类实例的父类指针.
5.如果父类里没有虚函数,则使用父类指针,只能访问父类的成员,而不能访问子类里的成员.
6.如果父类里的虚函数不是纯虚函数,且子类里没有重写该虚函数,则用父类指针访问该虚函数的时候,跟访问父类里的普通函数一样.
7.如果父类里的虚函数不是纯虚函数,且子类里重写了该虚函数,则用父类指针访问该虚函数的时候访问的是子类里重写后的函数.
8.如果父类里的虚函数是纯虚函数,则父类是个抽象类,子类要想能够被实例化,则必须重写该纯虚函数.用父类指针访问该纯虚函数的时候,访问到的是子类里重写了的函数.
9.再有一个要注意的是析构函数要声明为虚函数,这样在delete父类指针的时候,才会调用实例化的子类的虚函数,否则只会调用父类的析构函数,造成子类的剩余部分没被释放,从而造成内存的泄漏.