【C++】关于继承、多态的重点部分复习

来源:互联网 发布:ubuntu安装mysql.gz 编辑:程序博客网 时间:2024/05/21 06:21

关于继承和多态之前的博客中有讲到,但因为最近在复习C++的一些概念,所以把一些遗漏的同样是重点的知识点再简单整理一下,具体基础知识请戳链接:

继承:http://blog.csdn.net/chaseraod/article/details/69704143
多态:http://blog.csdn.net/chaseraod/article/details/69704143

1,C++的三大特性是什么?分别说说你的理解。
封装、继承、多态。
(1)封装:封装是C++面向对象的一大特性。是指隐藏对象的具体属性和实现方式,只对外界暴露公共接口。封装的好处:.将变化隔离;2.便于使用。4.提高安全性。
(2)继承:父类原有的特性在加上某种限制(public、protected、private)后,子类同样具有其特性,并且可以加上自己独有的特性,继承实现了复用性,在现实中的应用广泛。
(3)多态:多态是通过virtual关键字实现虚函数的重写,以及父类指针或引用实现动态绑定。

2,如何让一个类不能被继承?
这道题考查队继承的理解,子类的初始化一定会去调用父类的构造函数,因此当我们把父类的构造函数设为私有时,这个类就不能被继承。
代码:

class AA{  private:  AA()  {}  protected:  int _a; }class BB:public AA{  public:  BB()   {}  private:   int _b;

3,什么情况下必须把父类的析构函数设为私有,如果不设为私有,会有什么危害?
我们先来看以下代码:

class A{public:    ~A()    {        cout << "~A()" << endl;    }};class B :public A{public:    ~B()    {        cout << "~B()" << endl;    }};int main(){    A *p = new B;    delete p;    system("pause");    return 0;}

运行结果:
这里写图片描述

可以看到,程序运行后并没有调用子类的析构函数,如果在子类的析构函数中有其他的工作,那么造成的损失将会很大。这里了是因为析构函数不够成多态,所以在动态联编时只会调用父类的析构函数。所以,通常我们将父类的析构函数设为虚函数,这样防止内存泄漏的损失。

4,C++类的成员函数中重载、重写(覆盖)、重定义(隐藏)分别是什么?
重载:(1)在同一作用域
(2)函数名相同、参数不同
(3)返回值可以不同
重写:(1)不在同一作用域(分别在基类和派生类)
(2)函数名相同、参数相同、返回值相同(协变除外)
(3)基类函数必须有virtual关键字
(4)访问修饰符可以不同
重定义:(1)在不同作用域中
(2)函数名相同
(3)在基类和派生类中只要不构成重写就是重定义

5,虚继承和多态中虚函数的区别
(1)虚继承
相信很多初学者都会分不清虚继承中的virtual和多态中的virtual,很多情况下会将他们搞混,但是它们之间并没有一点联系。
我们来看下面的代码:

class A{public:    int _a;};class B :public A{public:    int _b;};class C :public A{public:    int _c;};class D :public B, public C{public:    int _d;};int main(){    D d;    d._a = 1;    d._b = 2;    d._c = 3;    d._d = 4;    system("pause\n");    return 0;}

上面的程序就是菱形继承:
这里写图片描述

程序一运行,会报这样的错
这里写图片描述
A类和B类中都有_a,就是菱形继承产生的二义性问题。虚继承就是解决菱形继承的二义性和数据冗余问题的。

class A{public:    int _a;};class B :virtual public A{public:    int _b;};class C :virtual public A{public:    int _c;};class D :public B, public C{public:    int _d;};int main(){    D d;    d._a = 1;    d._b = 2;    d._c = 3;    d._d = 4;    system("pause\n");    return 0;}

此时编译通过,程序中_a只有一份。
这里写图片描述

那么,类A、B、C、D的大小各是多少呢?
这里写图片描述

为什么B和C的大小会是12字节呢?
通过上述的调试过程我们也可以看到,B中有成员_a,_b . C中有成员_a,_c,本应该是8字节,却多出了四个字节。
因为它们都是虚继承,内存中实际上有一个虚基表用来保存每个成员的相对偏移量,虚基类成员必须放在对象的最底层。所以,多出的四个字节实际是指向这个虚基表的指针。

(2)多态中的虚函数

class AA{public:    AA()    {}    virtual ~AA()    {}    virtual void Print()    {        cout << "I am AA" << endl;    }private:    int _a;};class BB:public AA{public:    BB()    {}    ~BB()    {}    void Print()    {        cout << "I am BB" << endl;    }private:   int _b;};int main(){    AA *p = new AA;    AA *p1 = new BB;    p->Print();    p1->Print();    system("pause");    return 0;}

运行结果:
这里写图片描述

这里的virtual是为了实现动态联编以实现多态。那么,类A,B的大小又是多少呢?
这里写图片描述

这里多出的四个字节和上面的一样吗?不一样!!!
多态中有一个虚表,但这里的虚表不同于虚继承中的虚基表,这里的虚表指的是虚函数表,一个类中有虚函数,则这个类的所有对象都含有一个指向虚表的指针,且所有的虚表指针指向同一个虚表,因此多出的四个字节便是这虚表指针的大小。