0
#include <iostream>

class Vehicle { 
public:
    void greet() {
        std::cout << "Hello, I'm a vehicle";
    }
};

class Car : public Vehicle { 
public:
    void greet() {
        std::cout << "Hello, I'm a car";
    }
};

class Bike : public Vehicle { 
public:
    void greet() {
        std::cout << "Hello, I'm a bike";
    }
};

void receiveVehicle(Vehicle vehicle) {
    vehicle.greet();
}

int main() {
    receiveVehicle(Car());
    return 0;
}

ご覧のとおり、タイプのパラメーターをVehicle関数に送信しようとしていますgreet()

CarBikeのサブクラスですVehicle。彼らは上書きしますgreet()

ただし、「こんにちは、私は乗り物です」と表示されます。

これは、orのような特定のサブクラスではなく、receiveVehicletype のパラメーターを受け取るためだと思います。しかし、それが私が望んでいることです。この関数を のサブクラスで動作させたいのです。VehicleCarBikeVehicle

期待される出力が得られないのはなぜですか?

4

3 に答える 3

4

コードには 2 つの問題があります。

1)パラメータを呼び出すとreceiveVehicle(Car());、値によって受け入れられます。これは、スライシングに関連する問題が発生することを意味します。車からを構築するデフォルトのコピー コンストラクターVehicleが呼び出されます。Vehicleポインターまたは参照に変更して、インデントどおりに機能させます。

例えば:

void receiveVehicle(Vehicle& vehicle) {
    vehicle.greet();
}

int main() {
    Car aCar;
    receiveVehicle(aCar);
    return 0;
}

2)virtualポリモーフィック呼び出しは、基本メソッドがキーワードでマークされている場合にのみ行われます。greetしたがって、仮想にする必要があります。

class Vehicle { 
public:
    virtual void greet() {
        std::cout << "Hello, I'm a vehicle";
    }
};

厳密にするためconstに、必要に応じて使用することもできます。

class Vehicle { 
public:
    virtual void greet() const {   //change it also in subclasses
        std::cout << "Hello, I'm a vehicle";
    }
};

void receiveVehicle(const Vehicle& vehicle) {
    vehicle.greet();
}
于 2013-10-20T21:47:46.860 に答える
4

ポリモーフィックにできるのはポインタと参照のみです。基本クラスが派生クラスから構築され、派生としてのアイデンティティとすべての余分なデータ メンバーを失うスライシングが発生しています。

tl;dr : 関数を変更して a を受け入れるVehicle&(そしてパラメーターを非一時的なものにする) と、正常に動作します。また、デフォルトでは関数は非仮想であるためvirtual、基本クラスの関数定義の前に単語を追加する必要がありますvirtual void greet() { ... }(気づいてくれてありがとう、Diego)。

長い説明:

値がある場合、コンパイラはそれに割り当てるメモリ量を認識している必要があることに注意してください。派生クラスは基本クラスよりも大きくなる可能性があるため、派生クラスから基本クラスを構築すると、派生クラスが保持するデータが失われ (スライスされ)、基本クラスのデータのみが保持されます。

派生クラスにはメンバーがなく、したがって基本クラスよりも大きくありませんが、コンパイラは値がポリモーフィックにならないことを認識しているため、インスタンスの vtable から仮想関数を検索する必要はありません。関数を直接呼び出すだけで、静的な (非ポリモーフィック) 動作が発生し、基本クラスの関数が呼び出されます。

コンパイラが仮想変数を呼び出した場合に何が起こるかを考えてみてください。thisポインターは、スライスされたために派生データを持たないオブジェクトを指し、関数が派生メンバー変数にアクセスしようとすると、それらはそこにいないだろう!

于 2013-10-20T21:47:05.797 に答える
1
void receiveVehicle(const Vehicle &vehicle) {
    vehicle.greet();
}
// Make your `greet` method `const`

ポリモーフィズムが機能するようにするには、参照(リスクを知っている場合はポインター)で渡します。それ以外の場合Carは にスライスされVehicleます。

于 2013-10-20T21:48:45.833 に答える