首页 >Java >java教程 >C语言中的面向对象?从头开始实现接口。

C语言中的面向对象?从头开始实现接口。

Mary-Kate Olsen
Mary-Kate Olsen原创
2025-01-21 10:05:12653浏览

Orientação a Objetos em C? Implementando uma interface do zero.

我一直对计算机充满好奇,总是会想:“好吧,我知道怎么用,但它到底是怎么工作的?”在这个过程中,我经常会做个思想实验:如果让我从零开始实现它,我会怎么做?在本文中,我们将探讨接口在面向对象编程中的工作原理(使用Java),然后在C语言中实现一个简陋的接口版本。

让我们来看一个例子

我们的例子很简单:计算车辆的价格。如果是汽车,价格将根据其最高速度计算;如果是摩托车,价格将根据其排量计算。我们首先用接口定义车辆的行为:

<code class="language-java">public class Main {
    public interface Vehicle {
        Integer price();
    }
}</code>

这里没什么特别的,只是一个返回整数的方法。现在让我们实现汽车类:

<code class="language-java">public class Main {
    // ...

    public static class Car implements Vehicle {
        private final Integer speed;

        public Car(Integer speed) {
            this.speed = speed;
        }

        @Override
        public Integer price() {
            return speed * 60;
        }
    }
}</code>

很经典:一个构造函数和price方法的实现,将速度乘以60。现在,让我们实现摩托车类:

<code class="language-java">public class Main {
    // ...

    public static class Motorcycle implements Vehicle {
        private final Integer cc;

        public Motorcycle(Integer cc) {
            this.cc = cc;
        }

        @Override
        public Integer price() {
            return cc * 10;
        }
    }
}</code>

几乎一样,唯一的区别是现在我们将排量乘以10。然后,我们实现一个打印车辆价格的方法:

<code class="language-java">public class Main {
    // ...

    public static void printVehiclePrice(Vehicle vehicle) {
        System.out.println("$" + vehicle.price() + ".00");
    }
}</code>

没什么秘密。最后,我们的main方法:

<code class="language-java">public class Main {
    // ...

    public static void main(String[] args) {
        Car car = new Car(120);
        Motorcycle motorcycle = new Motorcycle(1000);

        printVehiclePrice(car);
        printVehiclePrice(motorcycle);
    }
}</code>
<code>$ java Main.java
00.00
000.00</code>

这就是我们想要达到的模型,但是现在要在C语言中从零开始实现。

我们该如何解决这个问题?

当我想到对象时,首先想到的是一组表示状态的数据和操作和管理该状态的方法。在C语言中表示数据集合最直接的方法是结构体。对于方法,最接近的方法是接收状态作为参数的函数。此状态将对应于类中的this,例如:

<code class="language-c">typedef struct {
    int height_in_cm;
    int weight_in_kg;
} Person;

float person_bmi(Person *person) {
    float height_in_meters = (float)person->height_in_cm / 100;
    float bmi =
        (float)person->weight_in_kg / (height_in_meters * height_in_meters);

    return bmi;
}</code>

在这里,我们在Person结构体中定义了一个人的数据,并使用这些数据进行简单的计算。这是我们在C语言中可以拥有的最接近类的结构。也许在结构体中使用函数指针也是一个好主意?好吧,这留到下一篇文章再讨论。

好的,我们有了一种类似类的结构。现在,我们如何在C语言中定义接口?如果仔细想想,编译器/解释器不会做魔法来猜测哪些类实现了接口。它可以在编译时确定这一点,并将我们使用接口的所有部分替换为具体的类型。在编译后的程序中,接口甚至不存在。

由于C语言编译器没有提供这种可能性,我们必须自己实现这个方案。我们需要知道所有实现我们接口的类型,并想办法使用这些实现的函数。

在C语言中实现接口

首先,让我们定义我们简陋接口的框架。我们将创建一个枚举,其中包含不同的实现和我们函数的签名。

<code class="language-java">public class Main {
    public interface Vehicle {
        Integer price();
    }
}</code>

在这里,我们定义了我们的枚举,其中包含我们稍后将要实现的实现。这看起来可能不像,但这部分非常重要。接下来,我们声明了vehicle_free函数(稍后将解释)和vehicle_price函数,我们希望在我们的“类”中实现这些函数。现在让我们来看汽车的实现:

<code class="language-java">public class Main {
    // ...

    public static class Car implements Vehicle {
        private final Integer speed;

        public Car(Integer speed) {
            this.speed = speed;
        }

        @Override
        public Integer price() {
            return speed * 60;
        }
    }
}</code>

car_init函数在内存中初始化一个新的“对象”Car。在Java中,这将通过new自动完成。在这里,我们需要手动完成。vehicle_free函数将用于释放之前初始化的任何“对象”分配的内存,使用car_free等实现。摩托车的实现非常相似:

<code class="language-java">public class Main {
    // ...

    public static class Motorcycle implements Vehicle {
        private final Integer cc;

        public Motorcycle(Integer cc) {
            this.cc = cc;
        }

        @Override
        public Integer price() {
            return cc * 10;
        }
    }
}</code>

几乎一样,只是现在我们用VEHICLE_MOTORCYCLE初始化,并乘以10。现在让我们来看打印车辆价格的函数:

<code class="language-java">public class Main {
    // ...

    public static void printVehiclePrice(Vehicle vehicle) {
        System.out.println("$" + vehicle.price() + ".00");
    }
}</code>

如此简单……这样看来,我们似乎没有做太多工作。现在,最后也是最重要的一点,我们必须实现我们在上面接口定义中声明的函数,还记得吗?幸运的是,我们甚至不需要考虑这个实现。我们总会有一个简单的穷举switch/case,仅此而已。

<code class="language-java">public class Main {
    // ...

    public static void main(String[] args) {
        Car car = new Car(120);
        Motorcycle motorcycle = new Motorcycle(1000);

        printVehiclePrice(car);
        printVehiclePrice(motorcycle);
    }
}</code>

现在我们可以使用我们所做的一切:

<code>$ java Main.java
00.00
000.00</code>
<code class="language-c">typedef struct {
    int height_in_cm;
    int weight_in_kg;
} Person;

float person_bmi(Person *person) {
    float height_in_meters = (float)person->height_in_cm / 100;
    float bmi =
        (float)person->weight_in_kg / (height_in_meters * height_in_meters);

    return bmi;
}</code>

成功了!但是你可能会想:“好吧,这有什么用?”

一个真实的用例

我最喜欢的项目类型之一是解析器,从解释器到简单的数学表达式解析器。通常,当您实现这些解析器时,会遇到称为AST(抽象语法树)的东西。顾名思义,它是一棵树,它将表示您正在处理的语法,例如,变量声明int foo = 10; 是AST的一个节点,它包含另外三个节点,一个类型节点,用于int,一个标识符节点,用于foo,以及一个表达式节点,用于10,该节点包含另一个值为10的整数节点。看到它有多复杂了吗?

当我们在C语言中这样做时,我们必须在包含多个字段的巨大结构体之间进行选择,以表示任何可能的AST节点,或者使用多个小型结构体实现抽象定义,每个结构体表示不同的节点,就像我们在这里用我们的“接口”所做的那样。如果您想查看一个简单的示例,在这个数学表达式解析器中,我实现了第二种方法。

结论

编译器或解释器所做的任何事情都不是魔法。尝试自己实现一些东西总是一个有趣的练习。希望这是一篇有益的阅读。谢谢!

以上是C语言中的面向对象?从头开始实现接口。的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn