C++ 虚函数、接口(纯虚函数)

虚函数

在继承时,子类如果要保留父类的函数func,但实现不同的功能,就需要重写该函数。但是如果我们想用父类类型的指针去访问子类的func时,执行的却是父类的func。为了解决该问题,需要指定父类的func为虚函数。

:用父类指针访问子类函数,是为了方便。比如:如果有n个类继承于一个类,多个子类都要重写父类中的同一个函数,并且需要一个统一调用这个函数的情况时,就会使用父类指针做参数,因为子类可以被父类指针所识别。虚函数就是指定父类当中的函数会在继承时被子类函数的新定义替换。

没有虚函数时:

class Entity
{
public:
    string GetName(){ return  "Entity";}
};

class Player : public Entity
{
private:
    string m_Name;
public:
//    Player(const string& name)
//    {
//        m_Name = name;
//    }
    Player(const string& name): m_Name(name){}
    
    string GetName(){ return m_Name;}
};

void PrintName(Entity* entity)
{
    cout<< entity->GetName()<<endl;
}
int main(){
    Entity* e = new Entity();
    PrintName(e);
    Player* p = new Player("chen");
    PrintName(p);
	// 输出Entity,Entity
}

使用虚函数时:

class Entity
{
public:
	// 虚函数
    virtual string GetName(){ return  "Entity";}
};

class Player : public Entity
{
private:
    string m_Name;
public:

    Player(const string& name): m_Name(name){}
    // override提示
    string GetName() override { return m_Name;}
};

void PrintName(Entity* entity)
{
    cout<< entity->GetName()<<endl;
}
int main(){
    Entity* e = new Entity();
    PrintName(e);
    Player* p = new Player("chen");
    PrintName(p);
	// 输出Entity,chen
}

override是一个用在子类重写函数的提示。在标注这个提示后,如果这个函数不是虚函数,在编译时会报错。比如函数名写错了和父类的不一样,或父类该函数没有指定virtual,方便减少错误。

虚函数的原理

虚函数是一种动态联编(dynamic dispatch),他通过虚函数表(v表)来实现编译。v表包含了所有虚函数的映射,使程序运行时,可以使用正确的子类中的重写函数。

虚函数是有运行成本的:(cpu性能差的嵌入式平台可能需要慎用)

  • 额外内存来存储v表(包括父类需要有一个指针指向v表)(空间损失)
  • 调用虚函数时,需要遍历一遍v表来确定映射哪个函数(时间损失)

接口(纯虚函数)

这个玩意存在于父类当中。他的出现定义了后续子类的共有函数,并且强迫子类去实现这个函数。如果没有实现则不能实例化。当然,定义接口的父类也不能实例化。

class Entity
{
public:
	// 接口没有函数体,而是直接等于0
    virtual string GetName() = 0;
};

class Player : public Entity
{
private:
    string m_Name;
public:

    Player(const string& name): m_Name(name){}
    // 实现接口,如果不实现则子类也不能实例化
    string GetName() override { return m_Name;}
};

void PrintName(Entity* entity)
{
    cout<< entity->GetName()<<endl;
}
int main(){
	// 实例化Entity类会编译错误
//    Entity* e = new Entity();
//    PrintName(e);
    Player* p = new Player("chen");
    PrintName(p);

}

小提示:
只要保证当前类中没有未实现的接口,该类就可以正常使用。
例如A中有接口,B继承A并实现了接口,C继承B但没有显式实现接口。C依旧可以实例化,如果调用接口的函数,则是调用B类实现的方式。这些按照继承的定义从父类去递推即可,而不须是显式实现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值