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

关于继承和多态之前的博客中有讲到,但因为最近在复习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的大小又是多少呢?
这里写图片描述

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

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值