首頁 >Java >java教程 >C語言中的物件導向?從頭開始實作介面。

C語言中的物件導向?從頭開始實作介面。

Mary-Kate Olsen
Mary-Kate Olsen原創
2025-01-21 10:05:12694瀏覽

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