栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Cherno C++ P28 C++接口(纯虚函数)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Cherno C++ P28 C++接口(纯虚函数)

YouTube视频链接

C++接口(纯虚函数)

本文是ChernoP28视频的学习笔记。
  C++纯虚函数本质上与其他语言(如Java或C#)中的抽象方法或接口相同。纯虚函数允许我们在基类中定义一个没有实现的函数,然后强制子类去实现该函数。
  如上节的Main.cpp代码

#include

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

class Player :public Entity
{
private:
	std::string m_Name;
public:
	Player(const std::string& name) :m_Name(name) {}

	std::string GetName()override { return m_Name; }
};

void PrintName(Entity* entity)
{
	std::cout << entity->GetName() << std::endl;
}

int main()
{
	Entity* e = new Entity();
	PrintName(e);

	Player* p = new Player("Cherno");
	PrintName(p);

	std::cin.get();
}

  Entity类中有一个虚函数GetName,然后我们重写了这个函数在Player类中。在基类中这个GetName函数有函数体,意味着在Player类中即便不重写,仍然可以调用Player.GetName,然后返回Entity字符串。
  但实际上我们可能想要强制子类,为特定的函数提供自己的定义。在面向对象的编程中,创建一个类由未实现的方法组成,然后强制子类去实际实现它们是非常正常的。这通常被称为接口,因此类中的接口只包含未实现的方法作为模板,因此我们不可能实例化这个类。

class Entity
{
public:
	virtual std::string GetName() = 0;
};
引入纯虚函数

  因此我们去掉GetName的方法体,写成等于0。注意这里依然是定义成virtual虚函数,但等于0本质上使它成为一个纯虚函数。这意味着它必须在子类中实现。我们就不在具有实例化Entity类的能力。

  我们必须给它一个子类,来实现这个函数。例如Player类,当然这里要提供一些字符串,此时编译通过。

  这是因为我们实际实现了GetName函数,如果我们决定注释掉第16行的实现,可以看到不能进行实例化了。我们只有在实现了所有纯虚函数以后,才能够实例化。

引入C++接口

  假设我们想要编写一个函数来打印这些类的名字,我写上void Print,我想写???类型作为参数,obj作为例子,然后要做的是打印类名,就像obj->GetClassName()这样。

void Print(? ? ? * obj)
{
	std::cout << obj->GetClassName() << std::endl;
}

  这里我们需要的是一个类型来保证有GetClassName这个函数,这就是所谓的接口。我们可以把???类当做Printable,然后创建一个新的类叫做Printable,它会创建一个public的virtual字符串函数,返回一个字符串,GetClassName函数是纯虚的。

class Printable
{
public:
	virtual std::string GetClassName() = 0;
};

  然后我们要让Entity去实现这个接口。此时Player已经是一个Entity了,所以不需要实现Printable接口。

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

  如果Player不是Entity的子类,我们需要添加一个逗号来实现这个接口。

class Player :public Entity,Printable
{
private:
	std::string m_Name;
public:
	Player(const std::string& name) :m_Name(name) {}

	std::string GetName()override { return m_Name; }
};

  虽然我们把Printable叫做接口,但它其实只是一个类。因为它不过有个纯虚函数而已。
  我们继续在Entity类添加一个GetClassName函数,此时无法实例化的问题就解决了。

  但还会出现一些问题,我们还没有为Player提供一个覆写函数,如果现在去打印,调用Print函数,分别使用e和p参数。我们会发现打印了两次Entity,因为我们还没有在Player中提供定义

  因此我们在第12行加上override,在第22行覆写函数GetClassName,就会得到正确的类名。

  所有这些都来自于一个Print函数,它接受Printable作为参数。它不管具体是什么类。
  我们也可以创建一个完全不同的类。比如A是Printable类的子类,A就必须有GetClassName函数,并且实现了Printable这个接口,即实现这个函数。

class A:public Printable
{
public:
	virtual std::string GetClassName()override { return "A"; }
};

  然后我们可以调用打印,因为A是一个Printable,它保证了A类有GetClassName函数。但不建议这么写代码因为会造成内存泄漏。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/710592.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号