对比Java中的abstract与C++中的virtual

3 篇文章 0 订阅

背景

今天在学习一个代码案例的时候,首先声明了一个类,这个类继承了了一个父类,其中一个方法前面带了visual关键字,且最后面还写上了= 0,如下

	virtual void xxxx(const xxx &param1, xxx &param2)=0;

刚学这个并不知道是什么意思,实现的时候直接全选复制到子类的头文件了。结果就报错:

错误	30	error C2259: “CRTBapiDemo”: 不能实例化抽象类

然后我双击错误信息点击去看了一下错误原因:

	46	IntelliSense:  不允许使用抽象类类型 "CRTBapiDemo" 的对象: 
            函数 "CRTBapiDemo::callback" 是纯虚拟函数

出身Java的我看到抽象类型无法实例化对象,就敏感起来了。于是我查了一下C++中的virtual,原来与Java中的abstract和interface有一定的类似,所以我想弄清楚这几者之间的关系。

Virtual是干什么的

虚函数是指一个类中你希望重载的成员函数 ,当你用一个基类指针或引用 指向一个继承类对象的时候,调用一个虚函数时, 实际调用的是继承类的版本。这是C++多态特性的一种体现形式。
观察以下代码:

#include<iostream>
using namespace std;
//基类
class ClassA
{
public:
	virtual void foo() { cout << "A::foo() is called" << endl; }
};
// 派生类(继承类)
class ClassB : public ClassA
{
public:
	virtual void foo() { cout << "B::foo() is called" << endl; }
};
int main()
{
	ClassA *ptr = new ClassB();
	ptr->foo();
	getchar();
	return 0;
}

结果是

B::foo() is called

由此可见,虽然ptr是ClassA的指针,但是执行foo()函数的时候,打印的却是ClassB的foo()。

virtual关键字的性质

c++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。因此,在子类从新声明该虚函数时,可以加,也可以不加,但习惯上每一层声明函数时都加virtual,使程序更加清晰。
例如:

class A
{
public:
    virtual void foo();
};

class B: public A
{
public:
    void foo();    // 没有virtual关键字!
};

class C: public B  // 从B继承,不是从A继承!
{
public:
    void foo();    // 也没有virtual关键字!
};

这种情况下,B::foo()是虚函数,C::foo()也同样是虚函数。

纯虚函数

菜鸟教程中是这样解释的:您可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。
换句话说,我们只是抽象了个函数模板,函数实现需要由不同的派生类去结合自己情况,具体实现
由此可以拓展出以下两点:

  • 含有纯虚函数的类被称为抽象类
    对于抽象的类来说,我们往往不希望它能实例化,因为实例化之后也没什么用,而对于一些具体的类来说,我们要求必须实现那些要求(纯虚函数),使之成为有具体动作的类
  • 仅含有纯虚函数的类称为接口类
    1、没有任何数据成员
    2、仅有成员函数
    3、成员函数都是纯虚函数

因此,一个函数声明为纯虚后,它的意思是:“我是一个抽象类!不要把我实例化!”纯虚函数用来规范派生类的行为,实际上就是所谓的“接口”。它告诉使用者,我的派生类都会有这个函数。
下面来测试一下,如果子类不去实现基类的纯虚函数,会发生什么:

class ClassA
{
public:
	virtual void foo() = 0;
};
// 派生类
class ClassB : public ClassA{};
int main()
{
	ClassA *ptr = new ClassB();//这里会报错
	ptr->foo();
	getchar();
	return 0;
}

编译器会直接在new ClassB()这里飘红:
ClassB飘红提示图

Java中的abstract

这里默认看到文章的人都像我一样,是在熟悉Java的基础上学的C++~

1、用abstract关键字来表达的类,其表达形式为:(public)abstract class 类名
2、抽象类不能被实例化,也就是说我们没法直接new 一个抽象类。抽象类本身就代表了一个类型,无法确定为一个具体的对象,所以不能实例化就合乎情理了,只能有它的继承类实例化。
3、抽象类虽然不能被实例化,但有自己的构造方法(这个后面再讨论)
4、抽象类与接口(interface)有很大的不同之处,接口中不能有实例方法去实现业务逻辑,而抽象类中可以有实例方法,并实现业务逻辑,比如我们可以在抽象类中创建和销毁一个线程池。
5、抽象类不能使用final关键字修饰,因为final修饰的类是无法被继承,而对于抽象类来说就是需要通过继承去实现抽象方法,这又会产生矛盾。

Java中,默认所有的函数都是虚函数,所以Java的多态比C++用起来更简洁,但是针对C++中的纯虚函数,Java中怎么体现呢?

interface类

一开始用注解的同学一定有过这样的疑问:

	@Autowire
	DemoService demoService;

“我注入了一个interface类,就可以直接用接口里的方法?!但是interface里没有方法体啊!”其实这是@Autowire注解的作用,它帮你找到该interface的实现类,注入进来的。这里咱们先不考虑注解,一个小疑问,就能看出interface的特点:

  • 只有方法名,没有方法体
  • 没有成员属性及构造方法
  • 需要自己写类来implement该接口类,且必须重写所有interface的方法

看到这里是不是感觉virtual xxx() = 0和interface类特别相似!你无法使用interface去实例化,并且你要继承我,你必须实现我定义的方法。

关系

所以C++中的virtual修饰方法以及virtual xxx() = 0,与Java里的abstract和interface的关系就清晰了。

  • 纯虚函数定义后必须被子类实现,类似interface,整个类称为抽象类
  • 没有声明纯虚函数的C++类不叫抽象类,即使有virtual修饰的虚函数,也可以实例化对象,而Java中的abstract抽象类和interface接口类都不可以实例化对象。
  • 普通虚函数,子类可以实现,也可以不实现。

其实声明了纯虚函数的C++类,就是Java中abstract和interface的结合体,不但和abstract一样可以有成员属性,而且子类必须重写纯虚函数。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值