P21 C++纯虚函数抽象接口

目录

前言

01 纯虚函数

02 纯虚函数必须被实现,才能创建这个类的实例。

03 测试代码

最后的话


前言

P20 C++虚函数与纯虚函数-CSDN博客

上一期我们学习了虚函数,本期我们学习一种特殊的虚函数,纯虚函数。

C++ 纯虚函数本质上与其他语言中的抽象方法或接口相同,基本上,纯虚函数允许我们在基类中定义一个没有实现的函数,然后强制子类去实现该函数。

我们可以看一下之前关于虚函数的例子。

你可以看到在 Animal类中有一个虚函数 GetName,然后我们在 Dog类中重写了那个函数,在基类中,这个 GetName 函数有函数体,意味着在某个类中重写它只是一个可选项,即使不重写它,仍然是可以调用它的。

然而在某些情况下,提供这种默认实现是没有意义的,实际上我们可能想要强制子类为特定的函数提供自己的定义,所以我们就有了纯虚函数。

01 纯虚函数

在面向对象编程中,创建一个类,只由未实现的方法组成,然后强制子类去实现它们的操作非常常见,这种类通常被称为接口。

因此,类中的接口只包含未实现的方法作为模板,由于这个接口实际上并不包含方法实现,我们实际上不可能实例化那个类,

让我们来看看这个在 Animol类中的 GetName 函数能不能搞成纯虚函数。

在上面的代码中,我们依然是将 GetName 函数定义为虚函数,但等于0使它成为一个纯虚函数,这意味着它必须在一个子类中实现,不然的话就会报错,给出的错误是:

不允许使用抽象类类型 "Animal" 的对象:C/C++(322)
29.cpp(22, 26): 函数 "Animal::getNmae" 是纯虚拟函数

首先,我们看一下 main 函数,可以看到现在不具有实例化 Animal类的能力,我们必须给它一个子类来实现这个函数。

使用 Dog 类。

你可以看到 Dog类是可以正常工作的。

这只是因为我们在 Dog类中实现了那个 GetName 函数,你可以试试注释掉 Dog中的 GetName 函数,你会发现 Dog也不能进行实例化了。

本质上,你只能在实现了所有这些纯虚函数之后,才能够实例化,或者在更上层次的类中完成。比如,Dog类是另一个类(Animal的子类)的子类,而这个类实现了 GetName 函数,那也是可以的。

02 纯虚函数必须被实现,才能创建这个类的实例。

让我们来看另外一个更好的例子。

在上面,我们创建一个新类,叫 Print,它只有这些内容,它会创建一个 public virtual 字符串函数,返回一个字符串,这个函数是纯虚函数。然后让 Animal实现print这个接口。

 

我们编写一个函数来打印这些类的类名,保证 obj 有GetName 函数。现在我们需要一个类型,可以提供 GetName 函数,这就是所谓的接口。

还有就是,我虽然把 Print 叫做接口 interface,但它其实只是一个类,所以还是叫 class 而 不是叫做 interface,因为它只不过有一个纯虚函数

现在所有的类都需要实现这个 GetCName 函数了。

我们无法实现实例化的问题已经解决了, 现在已经提供了那个功能。

可以看到,现在我们得到了正确的类名,所有的这些都来自一个 Print 函数。这个函数接受 Print 的指针 作为参数,它并不关心具体是什么类。

比如,我们可以创建一个完全不同的类,比如 cat,它是一个 Print类的子类,它必须有相应的函数。

我现在创建了一个全新的类,并且实现了 Print 这个接口,即实现了这个函数,像之前那样调用打印,你会看到结果是正常的。

03 测试代码

//此为本章的测试代码

#include <iostream>

class Print
{
public:
    virtual std::string getName() = 0;
};

class Animal: public Print
{
public:
    std::string getName()
    {
        return "Animal";
    }
};

class Dog: public Animal
{
public:
    std::string getName()
    {
        return "Dog";
    };
};

class Cat:public Print
{
    std::string getName()override{return "Cat";}
};

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

int main()
{
    Animal *animal = new Animal;
    Dog *dog = new Dog;
    Cat *cat = new Cat;
    print(cat);
    print(animal);
    print(dog);
    return 0;   
}

最后的话

这就是纯虚函数整个的工作原理,它知道任何 Printable 的东西,它们都有一个 GetClassName 函数去调用,如果你不实现这个函数,你就不能实例化这个类。这就是 C++ 中的接口,也就是 C++ 中的纯虚函数,它是非常有用的一个工具,可以被用在刚才的这个场景下,如果想要确保类都有一个特定的方法,那么可以将这个类作为参数放入一个通用的函数中,然后就可以调用这个方法或者做其他事情。

  • 24
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个简单的 C++ 函数,用于判断两条三维线段是否相交: ```cpp #include <iostream> #include <cmath> using namespace std; struct Point3D { double x, y, z; }; bool isIntersected(Point3D p1, Point3D p2, Point3D p3, Point3D p4) { Point3D p13, p43, p21; double d1343, d4321, d1321, d4343, d2121; double numer, denom; const double EPSILON = 0.0000001; p13.x = p1.x - p3.x; p13.y = p1.y - p3.y; p13.z = p1.z - p3.z; p43.x = p4.x - p3.x; p43.y = p4.y - p3.y; p43.z = p4.z - p3.z; if (fabs(p43.x) < EPSILON && fabs(p43.y) < EPSILON && fabs(p43.z) < EPSILON) return false; p21.x = p2.x - p1.x; p21.y = p2.y - p1.y; p21.z = p2.z - p1.z; if (fabs(p21.x) < EPSILON && fabs(p21.y) < EPSILON && fabs(p21.z) < EPSILON) return false; d1343 = p13.x * p43.x + p13.y * p43.y + p13.z * p43.z; d4321 = p43.x * p21.x + p43.y * p21.y + p43.z * p21.z; d1321 = p13.x * p21.x + p13.y * p21.y + p13.z * p21.z; d4343 = p43.x * p43.x + p43.y * p43.y + p43.z * p43.z; d2121 = p21.x * p21.x + p21.y * p21.y + p21.z * p21.z; denom = d2121 * d4343 - d4321 * d4321; if (fabs(denom) < EPSILON) return false; numer = d1343 * d4321 - d1321 * d4343; double mua = numer / denom; double mub = (d1343 + d4321 * mua) / d4343; Point3D pa, pb; pa.x = p1.x + mua * p21.x; pa.y = p1.y + mua * p21.y; pa.z = p1.z + mua * p21.z; pb.x = p3.x + mub * p43.x; pb.y = p3.y + mub * p43.y; pb.z = p3.z + mub * p43.z; double dist = sqrt((pa.x - pb.x) * (pa.x - pb.x) + (pa.y - pb.y) * (pa.y - pb.y) + (pa.z - pb.z) * (pa.z - pb.z)); if (dist < EPSILON) return true; else return false; } int main() { Point3D p1 = {0, 0, 0}; Point3D p2 = {1, 1, 1}; Point3D p3 = {0, 1, 0}; Point3D p4 = {1, 0, 1}; if (isIntersected(p1, p2, p3, p4)) { cout << "The two line segments intersect." << endl; } else { cout << "The two line segments do not intersect." << endl; } return 0; } ``` 该函数的输入是两个三维线段的端点坐标,输出是一个布尔值,表示它们是否相交。如果相交,返回 `true`,否则返回 `false`。 该函数使用了两条线段的参数方程,通过求解它们的交点来判断它们是否相交。如果交点在两条线段之间,则它们相交,否则它们不相交。 该函数还考虑了一些特殊情况,例如两条线段重合、平行、退化为点或直线等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@ChenPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值