C++基础(五) 多态

1 多态示例

#include <iostream>
using namespace std;


class Hero
{
public:
    //1 多态的必要条件1:要有virtual修饰的虚函数
    virtual int getAd()
    {
        cout << "SuperHero getAd ---> 10" << endl;
        return 10;
    }
};

class SuperHero : public Hero
{
public:
    //2 多态的必要条件2:继承父类并重写虚函数
    int getAd()
    {
        cout << "SuperHero getAd ---> 100" << endl;
        return 100;
    }
};

//3 多态的必要条件3:父类的指针指向子类对象
void fight(Hero *hero)
{
    hero->getAd();
}

int main(void){
    
    Hero hero;
    SuperHero superHero;
    
    fight(&hero);
    fight(&superHero);
    
    return 0;
}

执行结果

SuperHero getAd ---> 10
SuperHero getAd ---> 100

如果父类方法不用virtual关键字修饰

int getAd()
    {
        cout << "SuperHero getAd ---> 10" << endl;
        return 10;
    }

执行结果

SuperHero getAd ---> 10
SuperHero getAd ---> 10

2 虚析构函数

#include <iostream>
using namespace std;

class TestA
{
public:
    TestA(){
        cout << "TestA()" << endl;
        this->p = new char[64];
        memset(p, 0, 64);
        strcpy(p, "string in TestA");
    }
    
    virtual void print()
    {
       cout << " TestA print() ---> p = " << p << endl;
    }
    
    ~TestA()
    {
        cout << "~TestA()" << endl;
        if(p != NULL)
        {
            delete [] p;
            p = NULL;
        }
    }
    
private:
    char *p;
};

class TestB : public TestA
{
public:
    TestB(){
        cout << "TestB()" << endl;
        this->p = new char[64];
        memset(p, 0, 64);
        strcpy(p, "string in B");
    }
    
    void print()
    {
        cout << " TestB print() ---> p = " << p << endl;
    }
    
    //1 不加virtual关键字,当父类指向子类的对象析构时,将不会执行子类的析构函数
    ~TestB()
    {
        cout << "~TestB()" << endl;
        if(p != NULL)
        {
            delete [] p;
            p = NULL;
        }
    }
    
private:
    char *p;
};

void myDelete(TestA* p)
{
    delete p;
}

void test1()
{
    TestB *b = new TestB;
    b->print();
    myDelete(b);
    
}

int main(void){
    test1();
    return 0;
}

析构函数不加 virtual关键字,执行结果

TestA()
TestB()
 TestB print() ---> p = string in B
~TestA()

加virtual关键字后,执行结果

TestA()
TestB()
 TestB print() ---> p = string in B
~TestB()
~TestA()

3 多态的原理

3.1 虚函数表和vptr指针

(1) 虚函数的解释

1 当类中声明虚函数时,编译器会在类中生成一个虚函数表;
2 虚函数表示一个存储类成员值函数指针的数据结构,由编译器自动生成和维护
3 virtual成员函数会被编译器放入虚函数表中
4 存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr)

(2) 编译器确定func是否为虚函数

1 func不是虚函数: 编译器可直接确定被调用的成员函数(静态联编)
2 func是虚函数: 编译器根据对象的vptr指针,在所指向的虚函数表中查找func()函数并调用。(查找好调用在运行是完成,即动态联编)

(3) 执行效率对比

1 通过虚函数表指针vptr调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用的函数,
而普通函数在编译时就确定了调用的函数,在效率上,虚函数的效率要低很多,出于效率考虑,没有必要将素有函数都声明为虚函数
3.2 证明vptr指针的存在
#include <iostream>
using namespace std;

class Parent1
{
public:
    virtual void func(int a, int b)
    {
      
    }

private:
    int a;
};

class Parent2
{
public:
    void func(int a, int b)
    {
    
    }
    
private:
    int a;
};

int main(void){
    Parent1 p1;
    Parent2 p2;
    
    cout <<  "  sizeof(p1)  = " <<  sizeof(p1) << endl;
    cout <<  "  sizeof(p2)  = " <<  sizeof(p2) << endl;
    return 0;
}

执行结果

sizeof(p1)  = 16
sizeof(p2)  = 4

p2占用4个字节,是因为int类型占用4字节。
而p1占用int类型 + 指针类型 = 4 + 8 = 12字节。而结果是16,是因为这里有个字节对齐,需要偏移4字节。
具体可参考
#pragma pack()
可参考以下内容

#include <iostream>
using namespace std;

#pragma pack(1)
class Parent1
{
public:
    virtual void func(int a, int b)
    {
        
    }
    
private:
    int a;
};

class Parent2
{
public:
    void func(int a, int b)
    {
        
    }
    
private:
    int a;
};
#pragma pack()

int main(void){
    Parent1 p1;
    Parent2 p2;

    cout <<  "  sizeof(p1)  = " <<  sizeof(p1) << endl;
    cout <<  "  sizeof(p2)  = " <<  sizeof(p2) << endl;
    
    cout <<  "  sizeof(int*)  = " <<  sizeof(int*) << endl;
    return 0;
}

此时的执行结果

sizeof(p1)  = 12
sizeof(p2)  = 4
sizeof(int*)  = 8
3.3 vptr指针的初始化时机
1 对象在创建时,由编译器对vptr指针进行初始化
2 只有在对象的构造完全结束后,vptr的指向才最终确定
3 父类对象的vptr指向父类虚函数表
4 子类对象的vptr指向子类虚函数表
#include <iostream>
using namespace std;

class Parent
{
public:
    Parent(int a):a(a)
    {
        cout << "Parent(int a)" << endl;
        //1 虽然print()方式虚函数,但是此时vptr还没有初始化,所以调用的是父类的方法
        print();
    }
    
    
    virtual void print()
    {
        cout << "Parent print() a=  " << a << endl;
    }
    
private:
    int a;
};

class Child : public Parent
{
public:
    Child(int a, int b) : Parent(a),b(b)
    {
        cout << "Child(int a, int b)" << endl;
        print();
    }
    
    void print()
    {
        cout << "Child::print() b = " << b << endl;
    }
    
private:
    int b;
};
int main(void){
    Parent *p = new Child(10, 20);
    
    p->print();
    delete p;
    
    return 0;
}

执行结果

Parent(int a)
Parent print() a=  10
Child(int a, int b)
Child::print() b = 20
Child::print() b = 20
3.4 父类指针和子类指针的步长
#include <iostream>
using namespace std;
class Parent
{
public:
    Parent(int a)
    {
        this->a = a;
    }
    
    virtual void print()
    {
        cout << "Parent print() a = " << a << endl;
    }
    
protected:
    int a;
};

class Child : public Parent
{
public:
    Child(int a) : Parent(a)
    {
        
    }
    
    virtual void print()
    {
        cout << "Child print() a = " << a << endl;
    }
    
    int b;
};


int main(void){
    Child array[] = {Child(0), Child(1), Child(2)};
    
    Child *cp = &array[0];
    Parent *pp = &array[0];
    
    cp->print();
    pp->print();

    cp++;
    cp->print();

	//1 因为父类和子类的步长相等,所以父类的++操作也可以从数组中取值
    pp++;
    pp->print();
    return 0;
}

执行结果

Child print() a = 0
Child print() a = 0
Child print() a = 1
Child print() a = 1

4 纯虚函数和抽象类

4.2 纯虚函数说明
1 含有纯虚函数的类,称为抽象基类,不可实例化,即不能创建实例对象。存在的意义就是被继承,提供族类的公共接口。
2 纯虚函数只有声明,没有实现。都是以virtual void func() = 0;的形式
3 如果一个勒种声明了纯虚函数,而在派生类中没有对该函数的定义,则该虚函数在派生类中仍然为纯虚函数,派生类仍然为出纯虚基类
4.2 示例

纯虚函数的语法示例

virtual void func() = 0;

有纯虚函数的类即为抽象类

#include <iostream>
using namespace std;

class Shape
{
public:

    //1 纯虚函数 子类必须实现
    virtual double getCircleArea() = 0;

    virtual void print() = 0;
};

class Circle : public Shape
{
public:
    Circle(double r)
    {
        this->r = r;

    }

    //必须要实现抽象方法
    double getCircleArea()
    {
        return 3.14 * r * r;
    }

    //必须要实现抽象方法
    void print()
    {
        cout << "圆的面积是: " << this->getCircleArea() << endl;
    }

private:
    double r;
};

int main(void){
    Shape *sp = new Circle(10.0);
    sp -> print();
    return 0;
}

执行结果

圆的面积是: 314
4.3 c++中的接口
1 C++中没有类似Java中的接口类,可以用纯虚函数来实现接口类
2 接口类中只有函数原型的定义,不做具体实现
class Interface
{
public:
	virtual void func1() = 0;
	virtual void func2() = 0;
}

5 多态示例

5.1 示例1: 打篮球
#include <iostream>
using namespace std;

class BasketBallPlayer
{
public:
    virtual void play() = 0;
};

class James : public BasketBallPlayer
{
public:
    void play()
    {
        cout << "James 打篮球" << endl;
    }
};

int main(void){
    BasketBallPlayer *player = new James;
    player->play();
    delete player;
    return 0;
}

执行结果

James 打篮球
5.2 示例2:Animal
#ifndef Animal_hpp
#define Animal_hpp

#include <iostream>
using namespace std;

class Animal
{
public:
    Animal();
    virtual ~Animal();
    
    virtual void voice() = 0;
};

void AnimalVoice(Animal *ap);

#endif /* Animal_hpp */
#include "Animal.hpp"

Animal::Animal()
{
    cout << "Animal()" << endl;
}

Animal::~Animal()
{
    cout << "~Animal()" << endl;
}

void AnimalVoice(Animal *ap)
{
    cout << "动物开始叫了" << endl;
    ap->voice();
     cout << "动物叫完了" << endl;
}
#ifndef Cat_hpp
#define Cat_hpp
#include "Animal.hpp"
class Cat:public Animal
{
public:
    Cat();
    ~Cat();
    
    virtual void voice();
};

#endif /* Cat_hpp */
#include "Cat.hpp"
Cat::Cat()
{
    cout << "Cat()..." << endl;
}

Cat::~Cat()
{
    cout << "~Cat()..." << endl;
}

void Cat::voice()
{
    cout << "喵喵猫" << endl;
}
#ifndef Dog_hpp
#define Dog_hpp

#include "Animal.hpp"

class Dog : public Animal
{
public:
    Dog();
    ~Dog();
    
    virtual void voice();
};

#endif /* Dog_hpp */
#include "Dog.hpp"

Dog::Dog()
{
    cout <<  "Dog()" << endl;
}

Dog::~Dog()
{
    cout <<  "~Dog()" << endl;
}

void Dog::voice()
{
    cout <<  "汪汪汪" << endl;
}
#include "Animal.hpp"
#include "Dog.hpp"
#include "Cat.hpp"

int main(void)
{
    Animal *ap = new Dog;
    //1 调用全局函数
    AnimalVoice(ap);
    delete ap;

    cout << "=================" << endl;
    
    ap = new Cat;
    AnimalVoice(ap);
    delete ap;

    return 0;
}

执行结果

Animal()
Dog()
动物开始叫了
汪汪汪
动物叫完了
~Dog()
~Animal()
=================
Animal()
Cat()...
动物开始叫了
喵喵猫
动物叫完了
~Cat()...
~Animal()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值