C++深度解析教程笔记13-多态,对象模型,抽象类,接口,多重继承

第49课 - 多态的概念和意义

函数重写回顾
■父类中被重写的函数依然会继承给子类·
■子类中重写的函数将覆盖父类中的函数
■通过作用域分辩辨符(::)可以访问到父类中的函数

Child c;
Farent* p = &c;
c.Parent::print();//从父类中继承
c.print(); //在子类中重写
P->print();//父类中定义

面向对象中期望的行为
一根据实际的对象类型判断如何调用重写函数
一父类指针(引用)指向
·交类对象则调用交类中定义的函数
·子类对象则调用子类中定义的重写函数

C++语言直接支持多态的概念
一通过使用virtual关键字对多态进行支持
一被virtual声明的函数被重写后具有多态特性
一被virtual声明的函数叫做虚函数

在这里插入图片描述

实验-virtual的使用

#include <iostream>
#include <string>

using namespace std;

class Parent
{
public:
    virtual void print()//virtual   virtual void print()
    {
        cout << "I'm Parent." << endl;
    }
};

class Child : public Parent
{
public:
    void print()//也是虚函数 virtual  可加,可不加
    {
        cout << "I'm Child." << endl;
    }
};

void how_to_print(Parent* p)//父类指针
{
    p->print();     // 展现多态的行为
}

int main()
{
    Parent p;
    Child c;
    
    how_to_print(&p);    // Expected to print: I'm Parent.
    how_to_print(&c);    // Expected to print: I'm Child.
   // I'm Parent.
   //I'm Child.
    return 0;
}

在这里插入图片描述
多态的意义
一在程序运行过程中展现出动态的特性
一函数重写必须多态实现,否则没有意义
一多态是面向对象组件化程序设计的基础特性

实验-重载与多态

#include <iostream>
#include <string>

using namespace std;

class Parent
{
public:
    virtual void func()
    {
        cout << "void func()" << endl;
    }
    
    virtual void func(int i)
    {
        cout << "void func(int i) : " << i << endl;
    }
    
    virtual void func(int i, int j)
    {
        cout << "void func(int i, int j) : " << "(" << i << ", " << j << ")" << endl;
    }
};

class Child : public Parent
{
public:
    void func(int i, int j)
    {
        cout << "void func(int i, int j) : " << i + j << endl;
    }
    
    void func(int i, int j, int k)
    {
        cout << "void func(int i, int j, int k) : " << i + j + k << endl;
    }
};

void run(Parent* p)
{
    p->func(1, 2);     // 展现多态的特性
                       // 动态联编
}


int main()
{
    Parent p;
    
    p.func();         // 静态联编   void func()
    p.func(1);        // 静态联编void func(int i) :1
    p.func(1, 2);     // 静态联编void func(int i, int j) :(1, 2)
    
    cout << endl;
    
    Child c;
    
    c.func(1, 2);     // 静态联编void func(int i, int j) :3
    
    cout << endl;
    
    run(&p);//void func(int i, int j) :(1, 2)
    run(&c);//void func(int i, int j) :3
    
    return 0;
}

实验-多态的例子

#include <iostream>
#include <string>

using namespace std;

class Boss
{
public:
    int fight()
    {
        int ret = 10;
        
        cout << "Boss::fight() : " << ret << endl;
        
        return ret;
    }
};

class Master
{
public:
    virtual int eightSwordKill()//虚函数  virtual 
    {
        int ret = 8;
        
        cout << "Master::eightSwordKill() : " << ret << endl;
        
        return ret;
    }
};

class NewMaster : public Master//继承了
{
public:
    int eightSwordKill()
    {
        int ret = Master::eightSwordKill() * 2;
        
        cout << "NewMaster::eightSwordKill() : " << ret << endl;
        
        return ret;
    }
};

void field_pk(Master* master, Boss* boss)
{
    int k = master->eightSwordKill();
    int b = boss->fight();
    
    if( k < b )
    {
        cout << "Master is killed..." << endl;
    }
    else
    {
        cout << "Boss is killed..." << endl;
    }
}

int main()
{
    Master master;
    Boss boss;
    
    cout << "Master vs Boss" << endl;
    
    field_pk(&master, &boss);
//Master::eightSwordKill() : 8
//Boss::fight() : 10
//Master is killed...
    
    cout << "NewMaster vs Boss" << endl;
    
    NewMaster newMaster;
    
    field_pk(&newMaster, &boss);//  Master* 类型 ,要用虚函数,否则依然是8
//NewMaster::eightSwordKill() : 16
//Boss::fight() : 10
//Boss is killed...
    
    return 0;
}

小结

函数重写只可能发生在交类与子类之间
根据实际对象的类型确定调用的具体函数
virtulal关键字是C++中支持多态的唯一方式
被重写的虚函数可表现出多态的特性

第50课 - C++ 对象模型分析(上)

在这里插入图片描述
class是一种特殊的struct
一在内存中class依旧可以看作变量的集合
一class与struct遵循相同的内存对齐规则
一class中的成员函数与成员变量是分开存放的
·每个对象有独立的成员变量
·所有对象共享类中的成员函数

实验??(多了字节数)

#include <iostream>
#include <string>

using namespace std;

class A
{
    int i;
    int j;
    char c;
    double d;
public:
    void print()
    {
        cout << "i = " << i << ", "
             << "j = " << j << ", "
             << "c = " << c << ", "
            << "d = " << d << endl;
    }
};

struct B
{
    int i;
    int j;
    char c;
    double d;
};

int main()
{
    A a;
    
    cout << "sizeof(A) = " << sizeof(A) << endl;    // 20 bytes
    cout << "sizeof(a) = " << sizeof(a) << endl;
    cout << "sizeof(B) = " << sizeof(B) << endl;    // 20 bytes
     cout << "sizeof(double) = " << sizeof(double) << endl;  
    a.print();
    
    B* p = reinterpret_cast<B*>(&a);//强制类型转换
//reinterpret_cast 强制类型转换
//用于指针类型间的强制转换
//用于整数和指针类型间的强制转换
    
    p->i = 1;
    p->j = 2;
    p->c = 'c';
   p->d = 3;
    
    a.print();
    
    p->i = 100;
    p->j = 200;
    p->c = 'C';
   p->d = 3.14;
    
    a.print();
    
    return 0;
}
//sizeof(A) = 24 ???
//sizeof(a) = 24
//sizeof(B) = 24
//i = 8, j = 0, c = , d = 7.29739e-317
//i = 1, j = 2, c = c, d = 3
//i = 100, j = 200, c = C, d = 3.14

C++对象模型分析
■运行时的对象退化为结构体的形式
一所有成员变量在内存中依次排布
一成员变量间可能存在内存空
一可以通过内存地址直接访问成员变量
一访问权限关键学在运行时失效

类中的成员函数位于代码段中
■调用成员函数时对象地址作为参数隐式传递
■成员函数通过对象地址访问成员变量
■C++语法规则隐藏了对象地址的传递过程

实验-对象模型

#include <iostream>
#include <string>

using namespace std;

class Demo
{
    int mi;
    int mj;
public:
    Demo(int i, int j)
    {
        mi = i;
        mj = j;
    }
    
    int getI()
    {
        return mi;
    }
    
    int getJ()
    {
        return mj;
    }
    
    int add(int value)
    {
        return mi + mj + value;
    }
};

int main()
{
    Demo d(1, 2);
    
    cout << "sizeof(d) = " << sizeof(d) << endl;//8
    cout << "d.getI() = " << d.getI() << endl;//1
    cout << "d.getJ() = " << d.getJ() << endl;//2
    cout << "d.add(3) = " << d.add(3) << endl;//6
    
    return 0;
}

实验-C语言实现面向对象

//50-2.h
#ifndef _50_2_H_
#define _50_2_H_

typedef void Demo;

Demo* Demo_Create(int i, int j);
int Demo_GetI(Demo* pThis);
int Demo_GetJ(Demo* pThis);
int Demo_Add(Demo* pThis, int value);
void Demo_Free(Demo* pThis);

#endif
//50-2.c
#include "50-2.h"
#include "malloc.h"

struct ClassDemo
{
    int mi;
    int mj;
};

Demo* Demo_Create(int i, int j)
{
    struct ClassDemo* ret = (struct ClassDemo*)malloc(sizeof(struct ClassDemo));
    
    if( ret != NULL )
    {
        ret->mi = i;
        ret->mj = j;
    }
    
    return ret;
}

int Demo_GetI(Demo* pThis)
{
     struct ClassDemo* obj = (struct ClassDemo*)pThis;
     
     return obj->mi;
}

int Demo_GetJ(Demo* pThis)
{
    struct ClassDemo* obj = (struct ClassDemo*)pThis;
     
    return obj->mj;
}

int Demo_Add(Demo* pThis, int value)
{
    struct ClassDemo* obj = (struct ClassDemo*)pThis;
     
    return obj->mi + obj->mj + value;
}

void Demo_Free(Demo* pThis)
{
    free(pThis);
}
//main.c
#include <stdio.h>
#include "50-2.h"

int main()
{
    Demo* d = Demo_Create(1, 2);             // Demo* d = new Demo(1, 2);
    
    printf("d.mi = %d\n", Demo_GetI(d));     // d->getI();
    printf("d.mj = %d\n", Demo_GetJ(d));     // d->getJ();
    printf("Add(3) = %d\n", Demo_Add(d, 3));    // d->add(3);
    
    // d->mi = 100;报错
    
    Demo_Free(d);
    
    return 0;
}
/*
output:
d.mi = 1
d.mj = 2
Add(3) = 6
*/

小结

C++ 中的类对象在内存布局上与结构体相同
成员变量和成员函数在内存中分开存放
访问权限关键学在运行时失效
调用成员函数时对象地址作为参数隐式传递

第51课 - C++ 对象模型分析(下)

继承对象模型

在这里插入图片描述

实验-对象指针转换

#include <iostream>
#include <string>

using namespace std;

class Demo
{
protected:
    int mi;
    int mj;

};

class Derived : public Demo
{
    int mk;
public:
    Derived(int i, int j, int k)
    {
        mi = i;
        mj = j;
        mk = k;
    }
    
    void print()
    {
        cout << "mi = " << mi << ", "
             << "mj = " << mj << ", "
             << "mk = " << mk << endl;
    }
};

struct Test//public
{   
    int mi;
    int mj;
    int mk;
};

int main()
{
    cout << "sizeof(Demo) = " << sizeof(Demo) << endl;        //8 
    cout << "sizeof(Derived) = " << sizeof(Derived) << endl;  //12
    
    Derived d(1, 2, 3);
    Test* p = reinterpret_cast<Test*>(&d);
//reinterpret_cast 强制类型转换
//用于指针类型间的强制转换
//用于整数和指针类型间的强制转换
    
    cout << "Before changing ..." << endl;
    //继承的对象与普通对象
    d.print();
   
    p->mi = 10;
    p->mj = 20;
    p->mk = 30;

    
    cout << "After changing ..." << endl;
    
    d.print();
    
    return 0;
}
/*
sizeof(Demo) = 8
sizeof(Derived) = 12
Before changing ...
mi = 1, mj = 2, mk = 3
After changing ...
mi = 10, mj = 20, mk = 30
*/

多态对象模型

在这里插入图片描述
多态对象模型
■C++多态的实现原理

一当类中声明虚函数时,编译器会在类中生成一个虚函数表
一虚函数表是一个存储成员函数地址的数据结构
一虚函数表是由编译器自动生成与维护的
一virtual成员函数会被编译器放入虚函数表中
一存在虚函数时,每个对象中都有一个指向虚函数表的指针

在这里插入图片描述
在这里插入图片描述

实验-多态对象模型

#include <iostream>
#include <string>

using namespace std;

class Demo
{
protected:
    int mi;
    int mj;
public:
    virtual void print()//virtual 虚函数表指针占用8字节空间
    {
    {
        cout << "mi = " << mi << ", "
             << "mj = " << mj << endl;
    }
};

class Derived : public Demo
{
    int mk;
public:
    Derived(int i, int j, int k)
    {
        mi = i;
        mj = j;
        mk = k;
    }
    
    void print()
    {
        cout << "mi = " << mi << ", "
             << "mj = " << mj << ", "
             << "mk = " << mk << endl;
    }
};

struct Test//public
{
    void* p;//虚函数表指针
    
    int mi;

    int mj;
    int mk;
};

int main()
{
    cout << "sizeof(Demo) = " << sizeof(Demo) << endl;        //8 
    cout << "sizeof(Derived) = " << sizeof(Derived) << endl;  //12
    
    Derived d(1, 2, 3);
    Test* p = reinterpret_cast<Test*>(&d);
//reinterpret_cast 强制类型转换
//用于指针类型间的强制转换
//用于整数和指针类型间的强制转换
    
    cout << "Before changing ..." << endl;
    
    d.print();
    //虚函数表的指针修改值
    p->mi = 10;
    p->mj = 20;
    p->mk = 30;

    
    cout << "After changing ..." << endl;
    
    d.print();
    
    return 0;
}

实验-C语言实现多态-要梳理一下??

#ifndef _51_2_H_
#define _51_2_H_

typedef void Demo;//父类
typedef void Derived;//子类
Demo* Demo_Create(int i, int j);
int Demo_GetI(Demo* pThis);
int Demo_GetJ(Demo* pThis);
int Demo_Add(Demo* pThis, int value);
void Demo_Free(Demo* pThis);
//子类的3个函数
Demo* Derived_Create(int i, int j,int k);
int Derived_GetK(Derived* pThis);
int Derived_Add(Derived* pThis, int value);//虚函数

#endif

#include "51-2.h"
#include "malloc.h"
//6真正的虚函数
static int Demo_Virtual_Add(Demo* pThis, int value);

static int Derived_Virtual_Add(Demo* pThis, int value);//12

struct vtable//1.5  建立虚函数表类
{
    int (*pAdd)(Derived*, int);//2放入函数指针 ,要实现add函数的多态

};

struct ClassDemo
{
    struct vtable* vptr;//1虚函数表指针,考虑指针的类型
    int mi;
    int mj;
};
struct ClassDerived
{
    struct ClassDemo d;//实现继承
    int mk;
};
static struct vtable g_demo_vtbl={
    Demo_Virtual_Add
};//3建立虚函数变量  5.static隐藏此变量,外部文件不可访问
//9.真正的虚函数初始化pAdd指针

static struct vtable g_derived_vtbl={
    Derived_Virtual_Add
};//11.子类部分
Demo* Demo_Create(int i, int j)
{
    struct ClassDemo* ret = (struct ClassDemo*)malloc(sizeof(struct ClassDemo));

    if( ret != NULL )
    {
        ret->vptr=&g_demo_vtbl;//4 指针指向虚函数表  关联对象和虚函数表
        ret->mi = i;
        ret->mj = j;
    }

    return ret;
}

int Demo_GetI(Demo* pThis)
{
     struct ClassDemo* obj = (struct ClassDemo*)pThis;

     return obj->mi;
}

int Demo_GetJ(Demo* pThis)
{
    struct ClassDemo* obj = (struct ClassDemo*)pThis;

    return obj->mj;
}
//7 虚函数表指针指向的虚函数
static int Demo_Virtual_Add(Demo* pThis, int value)//case1
{
    struct ClassDemo* obj = (struct ClassDemo*)pThis;

    return obj->mi + obj->mj + value;

}
//case1 case2函数重复吗? 2是对外的用户使用的
//6分析具体的虚函数
int Demo_Add(Demo* pThis, int value)//case2
{
    struct ClassDemo* obj = (struct ClassDemo*)pThis;

    //return obj->mi + obj->mj + value;//8修改
    return obj->vptr->pAdd(pThis,value);
}

void Demo_Free(Demo* pThis)
{
    free(pThis);
}
Demo* Derived_Create(int i, int j,int k)
{
        struct ClassDerived* ret = (struct ClassDerived*)malloc(sizeof(struct ClassDerived));

    if( ret != NULL )
    {
        ret->d.vptr=&g_derived_vtbl;   //10.子类部分
        ret->d.mi = i;//子类赋值
        ret->d.mj = j;//子类赋值
        ret->mk=k;
    }

    return ret;
}
int Derived_GetK(Derived* pThis)
{
    struct ClassDerived* obj = (struct ClassDerived*)pThis;//强制类型转换

     return obj->mk;

}
static int Derived_Virtual_Add(Demo* pThis, int value)//13
{
    struct ClassDerived* obj = (struct ClassDerived*)pThis;

    return obj->mk + value;

}
int Derived_Add(Derived* pThis, int value)//虚函数
{
    struct ClassDerived* obj = (struct ClassDerived*)pThis;

    return obj->d.vptr->pAdd(pThis,value);//14.
   // return obj->mk+obj->d.mi +obj->d.mj+ value;


}

//main.c

#include "51-2.h"

#include <stdio.h>
void run(Demo* p,int v)
{
    int r=Demo_Add(p,v);
    printf("r=%d\n",r);

}


//C实现多态,面向对象
int main()
{
    Demo* p1=Demo_Create(1,2);
    Derived* c1=Derived_Create(11,22,100);
    printf("Demo_Add(p,3)=%d\n",Demo_Add(p1,3));//6
    printf("Derived_Add(c,3)=%d\n",Derived_Add(c1,3));//103
    run(p1,3);//6
    run(c1,3);//36   11+22+3 并不是期望的103,没有调用Derived_Add,而是调用Demo_Add
    //实现多态
    //6
    //103

    Demo_Free(p1);//C语言没有析构函数 ,手动释放内存

    Demo_Free(c1);

    return 0;
}




小结

继承的本质就是父子间成员变量的叠加
C++中的多态是通过虚函数表实现的
壶函数表是由编译器自动生成与维护的
虚函数的调用效率低于普通成员函数

第52课 - C++ 中的抽象类和接口

在现实中需要知道具体的图形类型才能求面积所以对概念上的“图形”求面积是没有意义的!

class shape 
{
public:
double area(){
return 0;}
};

Shape只是一个概念上的类型,没有具体对象!

在这里插入图片描述
什么是抽象类?
■面向对象中的抽象类
一可用于表示现实世界中的抽象概念
一是一种只能定义类型,而不能产生对象的类
一只能被继承并重写相关函数
一直接特征是相关函数没有完整的实现

纯虚函数的语法规则

class shape{
public:
virtual double area() = 0;
};

"=0"用于告诉编译器当前是声明纯虚函数,因此不需要定义函数体。

C++语言中没有抽象类的概念
C++中通过纯虚函数实现抽象类
纯虚函数是指只定义原型的成员函数
一个C++类中存在纯虚函数就成为了抽象类

Shape是现实世界中各种图形的抽象概念因此:
一程序中必须能够反映抽象的图形
一程序中通过抽象类表示图形的概念
一抽象类不能创建对象,只能用于继承

实验-纯虚函数

#include <iostream>
#include <string>

using namespace std;

class Shape
{
public:
    virtual double area() = 0;//纯虚函数  告诉编译器当前是声明纯虚函数,因此不需要定义函数体。
};

class Rect : public Shape//继承
{
    int ma;
    int mb;
public:
    Rect(int a, int b)
    {
        ma = a;
        mb = b;
    }
    double area()
    {
        return ma * mb;
    }
};

class Circle : public Shape
{
    int mr;
public:
    Circle(int r)
    {
        mr = r;
    }
    double area()
    {
        return 3.14 * mr * mr;
    }
};

void area(Shape* p)//父类指针
{
    double r = p->area();
    
    cout << "r = " << r << endl;
}

int main()
{
    Rect rect(1, 2);
    Circle circle(10);
    
    area(&rect);
    area(&circle);
    //r = 2
  //r = 314
    return 0;
}

满足下面条件的C++类则称为接口
一类中没有定义任何的成员变量
一所有的成员函数都是公有的
一所有的成员函数都是纯虚函数
一接口是一种特殊的抽象类

抽象类只能用作父类被继承
子类必须实现纯虚函数的具体功能
纯虚函数被实现后成为虚函数
如果子类没有实现纯虚函数,则子类成为抽象类

实验-接口

#include <iostream>
#include <string>

using namespace std;

class Channel
{
public:
    virtual bool open() = 0;
    virtual void close() = 0;
    virtual bool send(char* buf, int len) = 0;
    virtual int receive(char* buf, int len) = 0;
};

int main()
{
    return 0;
}

小结

抽象类用于描述现实世界中的抽象概念
抽象类只能被继承不能创建对象
C++中没有抽象类的概念
C++中通过纯虚函数实现抽象类
类中只存在纯虚函数的时成为接口
接口是一种特殊的抽象类

第53课 - 被遗弃的多重继承(上)

C++中是否允许一个类继承自多个父类?
多重继承的语法规则

class Derived : public BaseA,public Base, public Basec
{
//....
};

多重继承的本质与单继承相同!

C++支持编写多重继承的代码
一一个子类可以拥有多个交类
一子类拥有所有父类的成员变量
一子类继承所有交类的成员函数
一子类对象可以当作任意父类对象使用

实验-多个父类指针的指向位置

#include <iostream>
#include <string>

using namespace std;

class BaseA//父类
{
    int ma;
public:
    BaseA(int a)
    {
        ma = a;
    }
    int getA()
    {
        return ma;
    }
};

class BaseB//父类
{
    int mb;
public:
    BaseB(int b)
    {
        mb = b;
    }
    int getB()
    {
        return mb;
    }
};

class Derived : public BaseA, public BaseB//多重继承
{
    int mc;
public:
    Derived(int a, int b, int c) : BaseA(a), BaseB(b)
    {
        mc = c;
    }
    int getC()
    {
        return mc;
    }
    void print()
    {
        cout << "ma = " << getA() << ", "
             << "mb = " << getB() << ", "
             << "mc = " << mc << endl;
    }
};

int main()
{
    cout << "sizeof(Derived) = " << sizeof(Derived) << endl;    // 12
    
    Derived d(1, 2, 3);
    
    d.print();
    
    cout << "d.getA() = " << d.getA() << endl;
    cout << "d.getB() = " << d.getB() << endl;
    cout << "d.getC() = " << d.getC() << endl;
    
    cout << endl;
    
    BaseA* pa = &d;//父类指针指向子类对象
    BaseB* pb = &d;//父类指针指向子类对象
    
    cout << "pa->getA() = " << pa->getA() << endl;
    cout << "pb->getB() = " << pb->getB() << endl;
    
    cout << endl;
    
    void* paa = pa;
    void* pbb = pb;
    
    
    if( paa == pbb )//虽然是同一个对象,指向的对象内部的不同位置
    {
        cout << "Pointer to the same object!" << endl; 
    }
    else
    {
        cout << "Error" << endl;
    }
    
    cout << "pa = " << pa << endl;
    cout << "pb = " << pb << endl;
    cout << "paa = " << paa << endl;
    cout << "pbb = " << pbb << endl; 
    
    return 0;
}
/*
sizeof(Derived) = 12
ma = 1, mb = 2, mc = 3
d.getA() = 1
d.getB() = 2
d.getC() = 3

pa->getA() = 1
pb->getB() = 2

Error
pa = 0x61fde4
pb = 0x61fde8
paa = 0x61fde4
pbb = 0x61fde8
*/

在这里插入图片描述
在这里插入图片描述

实验-多重继承带来的冗余

#include <iostream>
#include <string>

using namespace std;

class People//父类
{
    string m_name;
    int m_age;
public:
    People(string name, int age)
    {
        m_name = name;
        m_age = age;
    }
    void print()
    {
        cout << "Name = " << m_name << ", "
             << "Age = " << m_age << endl;
    }
};

class Teacher :virtual public People//virtual public People      public People  case2
{
public:
    Teacher(string name, int age) : People(name, age)
    {
    }
};
  
class Student : virtual public People//virtual public People   public People  case2
{
public:
    Student(string name, int age) : People(name, age)
    {
    }
};

class Doctor : public Teacher, public Student//多重继承
{
public:
    Doctor(string name, int age) : Teacher(name, age), Student(name, age), People(name, age)//Teacher(name, age), Student(name, age), People(name, age)
    {
    }
//Doctor(string name, int age) : Teacher(name+"1", age+1), Student(name+"2", age+2)()  case2
};

int main()
{
    Doctor d("Delphi", 33);
    
    //d.print();//error: request for member 'print' is ambiguous
  d.Teacher::print();
d.Student::print();
//case2
//Name = Delphi1, Age = 34
//Name = Delphi2, Age = 35
//case3:虚继承 增加复杂性
d.print();

    return 0;
}

虚继承能够解决数据瓦余问题
中间层父类不再关心顶层交类的初始化
最终子类必须直接调用顶层父类的构造函数

在这里插入图片描述

在这里插入图片描述

小结

C++支持多重继承的编程方式
多重继承容易带来问题
一可能出现“同一入对象的地址不同”的情况
一虚继承可以解决数据亢余的问题
一虚继承的使得架构设计可能出现问题

第54课 - 被遗弃的多重继承(下)

在这里插入图片描述

实验-多重继承产生多个虚函数表,解决方法dynamic cast

#include <iostream>
#include <string>

using namespace std;

class BaseA
{
public:
    virtual void funcA()
    {
        cout << "BaseA::funcA()" << endl;
    }
};

class BaseB
{
public:
    virtual void funcB()
    {
        cout << "BaseB::funcB()" << endl;
    }
};

class Derived : public BaseA, public BaseB //多重继承
{

};

int main()
{
    Derived d;
    BaseA* pa = &d;//父类指针指向子类
    BaseB* pb = &d;//父类指针指向子类
   
//dynamic_cast强制类型转换
//一用于有继承关系的类指针间的转换
//一用于有交叉关系的类指针间的转换
//一具有类型检查的功能
//一需要虚函数的支持
cout << "sizeof(BaseA) = " << sizeof(BaseA) << endl;
    cout << "sizeof(BaseB) = " << sizeof(BaseB) << endl;
    cout << "sizeof(d) = " << sizeof(d) << endl;//2个虚函数表
    
    cout << "Using pa to call funcA()..." << endl;
    
    pa->funcA();
    
    cout << "Using pb to call funcB()..." << endl;
    
    pb->funcB();
   BaseB* pbe = (BaseB*)pa;    // oops!!
cout << "(BaseB*)pa-Using pbe to call funcB()..." << endl;
    
    pbe->funcB();

    BaseB* pbc = dynamic_cast<BaseB*>(pa);
    
    cout << "dynamic_cast<BaseB*>(pa)-Using pbc to call funcB()..." << endl;
    
    pbc->funcB();
    
    cout << endl;
    
    cout << "pa = " << pa << endl;
    cout << "pb = " << pb << endl;
    cout << "pbe = " << pbe << endl;
    cout << "pbc = " << pbc << endl;
    
    return 0;
}
/*
sizeof(BaseA) = 8
sizeof(BaseB) = 8
sizeof(d) = 16
Using pa to call funcA()...
BaseA::funcA()
Using pb to call funcB()...
BaseB::funcB()
(BaseB*)pa-Using pbe to call funcB()...
BaseA::funcA()
dynamic_cast<BaseB*>(pa)-Using pbc to call funcB()...
BaseB::funcB()

pa = 0x61fdf0
pb = 0x61fdf8
pbe = 0x61fdf0
pbc = 0x61fdf8
*/

在这里插入图片描述
在这里插入图片描述

实验-正确使用多重继承:单继承某类+多接口实现

#include <iostream>
#include <string>

using namespace std;

class Base
{
protected:
    int mi;
public:
    Base(int i)
    {
        mi = i;
    }
    int getI()
    {
        return mi;
    }
    bool equal(Base* obj)
    {
        return (this == obj);
    }
};

class Interface1//接口1
{
public:
    virtual void add(int i) = 0;
    virtual void minus(int i) = 0;
};

class Interface2//接口2
{
public:
    virtual void multiply(int i) = 0;
    virtual void divide(int i) = 0;
};

class Derived : public Base, public Interface1, public Interface2//多重继承
{
public:
    Derived(int i) : Base(i)
    {
    }
    void add(int i)
    {
        mi += i;
    }
    void minus(int i)
    {
        mi -= i;
    }
    void multiply(int i)
    {
        mi *= i;
    }
    void divide(int i)
    {
        if( i != 0 )
        {
            mi /= i;
        }
    }
};

int main()
{
    Derived d(100);
    Derived* p = &d;
    Interface1* pInt1 = &d;
    Interface2* pInt2 = &d;
    
    cout << "p->getI() = " << p->getI() << endl;    // 100
    
    pInt1->add(10);
    pInt2->divide(11);
    pInt1->minus(5);
    pInt2->multiply(8);
    
    cout << "p->getI() = " << p->getI() << endl;    // 40
    
    cout << endl;
    
    cout << "pInt1 == p : " << p->equal(dynamic_cast<Base*>(pInt1)) << endl;
    cout << "pInt2 == p : " << p->equal(dynamic_cast<Base*>(pInt2)) << endl;
    
    return 0;
}
/*
p->getI() = 100
p->getI() = 40

pInt1 == p : 1
pInt2 == p : 1
*/

正确使用多重继承
■一些有用的工程建设议
一先继承一个交类,然后实现多个接口
一父类中提供equal()成员函数
一equal()成员函数用于判断指针是否指向当前对象
一与多重继承相关的强制类型转换用dynamic_cast完成

小结

多继承中可能出现多人虚函数表指针
与多重继承相关的强制类型转换用dynamic_cast完成
工程开发中采用“单继承多接口”的方式使用多继承
父类提供成员函数用于判断指针是否指向当前对象

第55课 - 经典问题解析四

关于动态内存分配
new和malloc的区别是什么? delete和free的区别是什么?

在这里插入图片描述
■new关键字与malloc函数的区别
new关键字是C++的一部分
malloc是由C库提供的函数
-new以具体类型为单位进行内存分配
-malloc以字节为单位进行内存分配
new在申请内存空间时可进行初始化
-malloc仅根据需要申请定量的内存空间

实验-new&malloc

#include <iostream>
#include <string>
#include <cstdlib>//注释后 malloc报错

using namespace std;

class Test
{
    int* mp;
public:
    Test()
    {
        cout << "Test::Test()" << endl;
        
        mp = new int(100);
        
        cout << *mp << endl;
    }
    ~Test()
    {
        delete mp;
        
        cout << "~Test::Test()" << endl;
    }
};

int main()
{
cout << "before Test* pn = new Test  " << endl;
    Test* pn = new Test;
cout << "after Test* pn = new Test  " << endl;
cout << endl;
cout << "before Test* pm = (Test*)malloc(sizeof(Test))  " << endl;
    Test* pm = (Test*)malloc(sizeof(Test));
cout << "after Test* pm = (Test*)malloc(sizeof(Test)) " << endl;
    cout << endl;
cout << "before delete pn " << endl;
    delete pn;
cout << "after delete pn " << endl;
cout << endl;
cout << "before free(pm) " << endl;
    free(pm);
cout << "after free(pm)" << endl;
    
    return 0;
}
/*
before Test* pn = new Test
Test::Test()
100
after Test* pn = new Test

before Test* pm = (Test*)malloc(sizeof(Test))
after Test* pm = (Test*)malloc(sizeof(Test))

before delete pn
~Test::Test()
after delete pn

before free(pm)
after free(pm)
*/

关于动态内存分配
■delete和free的区别
-delete在所有C++编译器中都被支持
一free在某些系统开发中是不能调用
一delete能够触发析构函数的调用
-free仅归还之前分配的内存空间
一对象的销毁只能使用delete
-free不适合面向对象开发

在这里插入图片描述
构造函数是否可以成为虚函数?析构函数是否可以成为虚函数?
■构造函数不可能成为虚函数
一在构造函数执行结束后,虚函数表指针才会被正确的初始化
■析构函数可以成为虚函数
一建议在设计类时将析构函数声明为虚函数

实验-构造和析构可以是虚函数吗&构造和析构中不会发生多态

#include <iostream>
#include <string>

using namespace std;

class Base//父类
{
public:
    Base()//构造函数  virtual Base()  // error: constructors cannot be declared 'virtual' [-fpermissive]
    {
        cout << "Base()" << endl;
        
        func();//测试会不会发生多态
    }
    
    virtual void func() //虚函数  构造函数和析构函数中不能发生多态行为
    {
        cout << "Base::func()" << endl;
    }
    
    virtual ~Base()//虚函数析构函数
    {
        func();//测试会不会发生多态
        
        cout << "~Base()" << endl;
    }
};


class Derived : public Base//子类
{
public:
    Derived()
    {
        cout << "Derived()" << endl;
        
     func();//测试会不会发生多态
    }
    
    virtual void func()//
    {
        cout << "Derived::func()" << endl;
    }
    
    ~Derived()
    {
        func();//测试会不会发生多态
        
        cout << "~Derived()" << endl;
    }
};


int main()
{
    Base* p = new Derived();//父类指针指向子类对象
    
    // ...
    
    delete p;
    
    return 0;
}
/*
父类的析构函数不设置为虚函数输出:
Base()
Derived()
~Base()
期望释放的子类,父类
父类的析构函数设置为虚函数输出
Base()
Derived()
~Derived()     //不会跳过子类对象的析构
~Base()
父类的构造和析构函数调用虚函数 func() 的输出
Base()
Base::func()      未发生多态,直接调用类内部的函数
Derived()
~Derived()
Base::func()     未发生多态,直接调用类内部的函数
~Base()

父类、子类的构造和析构函数调用虚函数 func() 的输出
Base()
Base::func()
Derived()
Derived::func()   未发生多态,直接调用类内部的函数
Derived::func()   未发生多态,直接调用类内部的函数
~Derived()
Base::func()
~Base()


*/

构造函数中是否可以发生多态?析构函数中是否可以发生多态?
构造函数中不可能发生多态行为
一在构造函数执行时,虚函数表指针未被正确初始化
析构函数中不可能发生多态行为
一在析构函数执行时,虚函数表指针已经被销毁

继承中如何正确的使用强制类型转换?

构造函数和析构函数中不能发生多态行为,只调用当前类中定义的函数版本!!

关于继承中的强制类型转换
■编译器会检查dynamic_cast的使用是否正确
■类型转换的结果只可能在运行阶段才能得到

关于继承中的强制类型转换
■dynamic_cast是与继承相关的类型转换关键字
■dynamic_cast要求相关的类中必须有虚函数
■用于有直接或者间接继承关系的指针(引用)之间
-指针:
·转换成功:得到目标类型的指钳
·转换失败:得到一个空指针
-引用:
·转换成功:得到目标类型的引用·
转换失败:得到一个异常操作信息

实验-dynamic_cast的使用

#include <iostream>
#include <string>

using namespace std;

class Base//父类
{
public:
    Base()
    {
        cout << "Base::Base()" << endl;
    }
    //~Base()未设置为虚函数
   virtual ~Base()//设置为虚函数
    {
        cout << "Base::~Base()" << endl;
    }
};

class Derived : public Base//子类
{

};

int main()
{
    //case1:
   //Base* p = new Derived;//父类指针指向子类
    
    //Derived* pd = dynamic_cast<Derived*>(p);//父类内部没有虚函数error: cannot dynamic_cast 'p' (of type 'class Base*') to type 'class Derived*' (source type is not polymorphic)
//Derived* pd = dynamic_cast<Derived*>(p);//有虚函数后 编译通过
 //cout << "pd = " << pd << endl;//0x632180
//dynamic_cast强制类型转换
//一用于有继承关系的类指针间的转换
//一用于有交叉关系的类指针间的转换
//一具有类型检查的功能
//一需要虚函数的支持
    
//case2:
Base* p = new Base;//父类指针指向父类
Derived* pd = dynamic_cast<Derived*>(p);
cout << "pd = " << pd << endl;//0  转换失败了  子类指针不能指向父类

    if( pd != NULL )
    {
        cout << "pd = " << pd << endl;
    }
    else
    {
        cout << "Cast error!" << endl;
    }
    
    delete p;/**/
    
    return 0;
}


小结

new/delete会触发构造函数或者析构函数的调用
构造函数不能成为虚函数
析构函数可以成为虚函数
构造函数和析构函数中都无法产生多态行为
dynamic_cast是与继承相关的专用转换关键字

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值