C++的interface与抽象类

提示:文章

前言

前期疑问:
本文目标:


一、背景

本文表述了几个概念关键词,包括接口interface,abstract关键词、抽象类、抽象函数,虚函数、纯虚函数、extend关键词

二、C++的interface与抽象类

在看享元模式的时候,看的文章里有的是用java实现的,有的是用c++实现的,提到一个interface的概念。之前我是看过这个的。按照我之前的记忆,c++是没有interface的。但是又记不清了,今天再次看一下。

查了资料,c++是没有interface这个关键词的。

c++ 有interface关键词吗?

没有

C++中没有interface关键词。C++的设计哲学强调灵活性和效率,因此没有像Java或C#那样提供专门的interface关键字。在C++中,接口的概念通常通过抽象类来实现,抽象类包含纯虚函数,这些函数只有声明而没有实现‌。

那么又涉及到一个问题?什么是抽象类?

我印象中好像是用abstract修饰类的。但是实际上我记错了。

c++抽象类有abstract关键词吗?

‌在C++中,没有abstract这个关键字来定义抽象类。C++中的抽象类是通过包含至少一个纯虚函数的类来定义的。纯虚函数是在基类中声明但没有实现的虚函数,其声明方式是在函数声明的末尾加上= 0

所以c++抽象类和abstract没有关系。那什么是抽象类?

其实上述的解释已经说明了,c++抽象类是包含至少一个纯虚函数的类。

我印象中之前还做过一个抽象类和纯虚函数的题目。找一下看看。

题目1

1、以下关于抽象类的描述正确的是

A、不能实例化指向抽象类对象的指针或引用

B、抽象类的纯虚函数的实现可以由自身给出,也可以由派生类给出

C、可以实例化抽象类对象

D、抽象类的纯虚函数的实现由派生类给出

这个当时没有写答案,现在看答案ABD都对啊。。。

正确答案:D

A、在C++中,不能实例化指向抽象类对象的指针或引用

因为抽象类本身不能被实例化。抽象类是包含至少一个纯虚函数的类,纯虚函数是没有具体实现的虚函数,其函数声明以“= 0”结束。由于抽象类没有完整的实现,因此不能直接创建其实例。

解决方法

  1. ‌子类化抽象类:创建一个子类继承自抽象类,并实现其纯虚函数。这样,子类可以被实例化,并且可以使用子类对象来访问抽象类中的非纯虚函数‌。
  2. ‌使用指针或引用‌:虽然不能直接实例化抽象类,但可以创建指向抽象类的指针或引用,并将其指向具体的子类对象。通过指针或引用可以访问抽象类中的非纯虚函数‌。

声明抽象类的指针代码实例

class AbstractClass {
public:
    virtual void pureVirtualFunction() = 0;
    void nonPureVirtualFunction() {
        // 实现非纯虚函数的逻辑
    }
};

class ConcreteClass : public AbstractClass {
public:
    void pureVirtualFunction() {
        // 实现纯虚函数的逻辑
    }
};

int main() {
    ConcreteClass obj;
    AbstractClass* ptr = &obj;
    ptr->pureVirtualFunction(); // 通过指针调用纯虚函数
    ptr->nonPureVirtualFunction(); // 通过指针调用非纯虚函数
    return 0;
}

从上述解决方法表述中可以看出A选项错误,就是可以声明抽象类的指针

B错误

抽象类的纯虚函数的实现不能由自身给出,必须由派生类给出‌。抽象类是用来声明一组派生类共同操作的接口,其纯虚函数的实现必须由派生类来完成。‌

抽象类是一种特殊的类,它包含至少一个纯虚函数。纯虚函数是没有函数体的虚函数,其函数名后面会加上= 0。抽象类不能直接创建对象,只有当派生类重写了所有纯虚函数后,才能创建派生类的对象。

在C++中,抽象类的析构函数可以是纯虚函数,但为了实例化派生类对象,仍然需要在抽象基类中提供析构函数的函数体。然而,这并不改变纯虚函数实现的一般规则,即除了析构函数外,其他纯虚函数的实现必须由派生类完成。

题目2

在探究这个问题的时候遇到其他新题目

对抽象类的描述正确的是()

A、抽象类的方法都是抽象方法

B、一个类可以继承多个抽象类

C、抽象类不能有构造方法

D、抽象类不能被实例化

正确答案:D

来源:牛客网

选项A错误,抽象类可以包含普通方法。

选项B错误,Java中的继承是单继承,一个子类最多只能有一个父类。

抽象类到底能不能被实例化是初学者很容易犯的错误,抽象类确实有构造方法,但这个构造方法是用来被子类调用的,因为任何子类都必须调用从Object开始的所有父亲的构造方法,才算完成初始化工作。如果抽象类被实例化,就会报错,编译无法通过。而接口里不包含构造器,自然无法被实例化。因此选项C错误,选项D正确

上述B选项还要探究一下

由上述题目,又引出一个概念

什么是抽象函数?

C++中的抽象函数‌是指那些在基类中声明了但没有具体实现的虚函数,通常用= 0来标记。这些函数被称为纯虚函数,因为它们不包含函数体,只是提供了一个接口,要求派生类必须实现这些函数。抽象函数主要用于实现多态和接口继承,确保派生类提供具体的实现‌

代码实例

// 抽象函数的例子
// 假设有一个基类Animal,定义了一个纯虚函数MakeSound():
class Animal {
public:
    virtual void MakeSound() = 0; // 纯虚函数
};

// 派生类Dog和Cat需要实现这个纯虚函数:
class Dog : public Animal {
public:
    void MakeSound() override {
        std::cout << "Bark" << std::endl;
    }
};

class Cat : public Animal {
public:
    void MakeSound() override {
        std::cout << "Meow" << std::endl;
    }
};
// 这样,Dog和Cat类分别实现了MakeSound()方法,从而可以被实例化。

抽象函数和纯虚函数的区别?

按照百度给的代码实例来看,纯虚函数就是抽象函数

class AbstractClass {
public:
virtual void interfaceFunction() = 0; // 抽象函数,没有提供具体实现
virtual ~AbstractClass() {} // 虚析构函数
};

class ConcreteClass : public AbstractClass {
public:
void interfaceFunction() override {
// 实现抽象函数的具体代码
}
};

int main() {
AbstractClass* ptr = new ConcreteClass();
ptr->interfaceFunction();
delete ptr;
return 0;
}

纯虚函数和虚函数的区别

c++纯虚函数和虚函数

在C++中,虚函数和纯虚函数都用于实现多态性。

  1. 虚函数:

虚函数在基类中定义,但它们在派生类中可以被重写。基类中定义的虚函数可以在派生类中被重写,以实现不同的功能。

class Base {
public:
    virtual void fun() {
        cout << "Base fun()" << endl;
    }
};
 
class Derived : public Base {
public:
    void fun() {
        cout << "Derived fun()" << endl;
    }
};
  1. 纯虚函数:

在基类中定义为纯虚函数的成员函数,它在基类中没有定义,要求在基类的派生类中必须重写。

class Base {
public:
    virtual void fun() = 0;
};
 
class Derived : public Base {
public:
    void fun() {
        cout << "Derived fun()" << endl;
    }
};

在这里,fun()是一个纯虚函数。在Base类中,我们没有提供fun()的定义,而是在Derived类中提供了fun()的定义。

注意:

  • 包含纯虚函数的类被称为抽象类,无法实例化。
  • 派生类必须提供纯虚函数的定义,除非它也是抽象类。
  • 如果派生类没有提供纯虚函数的定义,则编译器将报错。

为什要有虚函数?

查资料说虚函数可以实现动态绑定,而不是在静态编译器绑定。

我思考一下,也许是这样

class BaseClasss
{
    virtual void func();
}

class DerivedClassA : public BaseClase
{
    void func();
}

class DerivedClassB : public BaseClase
{
    void func();
}

void VirtualFuncVerify(BaseClasss* baseClass)
{
    baseClass->func();		// 调用基类还是派生类的func函数,取决于VirtualFuncVerify函数入参是类DerivedClassA对象还是类DerivedClassB对象。这边实现了动态绑定
}

// 上述代码是我自己写的,没有写构造函数和析构函数。也没有写func函数的实现

上述表述还有一个问题就是,虽然是动态传参了,在函数体内可以动态调用函数。但是实际VirtualFuncVerif函数的实参是根据自己的需要生成的,那么既然已经动态生成对象了,为什么不直接在函数中动态指定函数调用呢?这是个问题。查下资料看下。

这篇文章似乎并没有解释清楚为什么实现了动态编联,但是介绍了虚函数表,记录一下。C++虚成员函数与动态联编

2024年12月9日15:58:28

为什么要使用基类和派生类

刚才突然在想,为什么要有基类和派生类。因为不同的类会有相同的属性。比如猫和狗会都有动物的属性。但是他们又有不同的属性。比如猫吃鱼狗吃肉。所以派生类又根据不同的属性在派生类中实现。

三、extends和implements

2.1 c++中有extends关键词吗?什么时候使用extends关键词

C++什么时候使用extends

在C++中,没有类似Java中的extends关键字的概念。C++使用publicprotectedprivate关键字来控制继承和访问权限,但是它使用的是单一继承和多重继承机制,通过冒号:来指定基类。

如果你想创建一个新类,它从另一个已存在的类派生,你可以这样做:

class BaseClass {
 // 基类成员
public:
 void baseMethod() {
     // 实现
 }
};

class DerivedClass : public BaseClass {
 // 派生类成员
public:
 void derivedMethod() {
     // 实现
 }
};

在这个例子中,BaseClass是基类,DerivedClass是派生类。它们之间的继承关系通过冒号后的关键字public来指定,这里表示公有继承。

如果你想要实现多重继承,可以这样:

class Base1 {
 // 基类1成员
};

class Base2 {
 // 基类2成员
};

class Derived : public Base1, public Base2 {
 // 派生类成员
};

在这个例子中,Derived类同时继承了Base1Base2

请注意,C++支持多种继承方式,包括:

  • 公有继承(public):派生类继承基类的所有公有和保护成员,并且它们在派生类中仍然是公有和保护的。
  • 私有继承(private):派生类继承基类的所有公有和保护成员,但在派生类中它们都变成私有成员。
  • 保护继承(protected):派生类继承基类的所有公有和保护成员,但它们在派生类中都变成保护成员。

在实际编程中,你需要根据具体需求选择合适的继承方式。

2.2 extends和implements的概念和区别

翻译:extends翻译延伸 implements翻译实施

1、Java的extends(继承)与implements(接口)

百度安全验证

2、c++

C++中用 :extends来表示继承关系。

c++中好像没有implements这个关键字。

这个帖子里有介绍final和this的用法,还介绍了c++的struct和class。

还介绍了override的用法:【override 关键字是 C++11 引入的,用来在派生类中成员函数声明时明确表明需要派生类去重写的那些成员方法,这样如果程序员在成员方法实体定义中做的不对编译器可以报错提醒】

C++学习笔记9 - 类与面向对象中的关键字_c++面向对象关键字__Amen的博客-CSDN博客

这个帖子介绍了子类和基类的兼容性规则:

c++:继承(1)_c++ extends-CSDN博客

1、派生类的对象可以赋值给基类的对象。

2、派生类对象的地址可以赋值给其基类的指针变量

3、派生类对象可以初始化基类的引用


总结

未完待续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值