继承和多态

继承和多态

C++三大特性:封装,继承,多态

封装:通过访问权限的控制(private,protect,public) 将成员变量变得不可见,

继承:一个类继承另外一个类的成员变量和成员函数

被继承的类:父类(基类)

继承的类:子类(派生类)

继承方式:

1、public:公有继承 public->public protected->protected private->private

class Student:public Person    //其中public代表继承权限

(1)父类的私有成员可以被继承,但是不可见

(2)子类可以调用父类的变量进行初始化,但这个函数只能在初始化列表中调用

using CStrRef = const std::string &;
public:
     Student(CStrRef name, int age, CStrRef ID,int score):Person(name,age,ID),score(score){}

(3)父类先构造,子类先析构

(4)继承中的名词遮蔽:父类与子类定义同名函数,子类对象只能调用的是子类函数,如果非要使用,要用域解析符

s.Person::show();

2、proteced:保护继承 public->protected protected->protected private->private

class Student:protected Person

3、private:私有继承 public->private protected->private private->private

class Student:private Person

一般都用public,private几乎不使用

开闭原则:对修改关闭,对扩展开发

4、多继承

class Derived:public Base1,public Base2     //以 , 隔开继承两个父类

父类构造顺序和构造函数调用顺序无关,和继承顺序有关

二义性问题:当两个父类都定义一个同名变量时,子类调用会出现定义不明确的问题

解决菱形继承的二义性问题:虚继承 virtual

class Base1:virtual public ULtraBase

虚继承指针->虚继承表(虚继承的类的变量相对于本类的偏移量)

5、组合

将要使用的类的对象,塞入当前类中

class Base1
{
    public:
    Base1(int a):a(a){}
    void show()
    {
        std::cout << "a:" << a;
    }
    protected:
        int a;
};
class Base2
{
    public:
    Base2(int b):b(b){}
    void show()
    {
        std::cout << "b:" << b;
    }
    protected:
        int b;
};
class Derived
{
public:
Derived(int a,int b):b1(a),b2(b){}
void show()
{
    b1.show();
    b2.show();
}
private:
    Base1 b1;   //在需要使用其他类的类中创建需要使用的类的对象
    Base2 b2;
};

多态:

相同的代码,不同的表现形式

目的:提高代码的可扩用性

静多态:在编译期进程函数的地址绑定(早绑定) C多态:函数指针(回调) C++多态:函数重载

动多态:在运行期才真正确定函数的地址(迟绑定) C++:虚函数virtual

虚函数指针:虚函数表

virtual void show()

重写:子类函数和基类函数完全相同(返回值,函数名,参数列表)

只有重写的情况下,才能构成多态

重载,重写,名词遮蔽的概念一定要分清。

构成多态的要素:

1、基类指针指向子类对象

2、基类要有虚函数

3、子类重写父类虚函数

纯虚函数:

定函数的格式

抽象类:具有纯虚函数的类,不允许实例化对象

//具有纯虚函数的类:抽象类:不允许实例化对象
class Sort
{
public:
    virtual void sort(int *a, int size) = 0;//纯虚函数
};
class Base
{
    public:
    virtual void show()
    {
        std::cout << "Base" << std::endl;
    }
    protected:
        int a;
};
class Derived1:public Base
{

    public:
    void show()
    {
        std::cout << "Derived1" << std::endl;
    }
};
class Derived2:public Base
{

    public:
    void show()
    {
        std::cout << "Derived2" << std::endl;
    }
};
int main()
{
    //向上转型
    // Derived *d=new Derived;//基类指针,指向子类对象
    // Base *b = d;
    // b->show();
    // d->show();
    Base *a[2];
    a[0] = new Derived1;
    a[1] = new Derived2;
    //多态
    for (int i = 0; i < 2;i++)
    {
        a[i]->show();

    }
        return 0;
}

虚析构函数的作用:

向上转型时,释放基类指针,不会调用子类析构,造成子类资源无法释放,基于这个原因,我们把父类析构函数定义为虚函数

class Base
{
    public:
    Base()
    {
        std::cout << "父类构造\n";
        a = new int;

    }
    virtual ~Base() //虚析构函数
    {
        std::cout << "父类析构\n";
        delete a;
    }

    private:
        int *a;
};
class Derived:public Base
{
    public:
    Derived():Base()
    {
        std::cout << "子类构造\n";
        b = new int;
    }
    ~Derived()
    {
        std::cout << "子类析构\n";
        delete b;
    }

    private:
        int *b;
};
int main()
{
    Base *d = new Derived;
    delete d;
    return 0;
}

类的内存空间分配

不同的对象应该具有不同的内存地址,空类占一个字节。

静态变量不占类型,函数不占,虚函数会有一个虚函数表占8个字节

基类和子类之间不能拼接空间

创建虚函数指针之后,编译器会自己进行优化内存,会使基类和子类的空间以及可以拼接的空间进行拼接

虚函数表是一个类一张表,虚函数指针指向表吗,表里可以储存多个虚函数

g++编译器内存存储顺序:1、有虚继承、虚函数:虚继承指针->子类变量->虚函数指针->基类变量

​ 2、有虚函数,无虚继承:虚函数指针->基类变量->子类变量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值