2016-10-25 5 views
1

は、私は基本抽象クラスを考えてみましょう基本クラスオブジェクトは、派生クラスの種類を推定する方法

class Test 
{ 
public: 
vector< Base* > base_vector; 
}; 

質問1:基本クラスのオブジェクトへのポインタのベクトルがある場合は、派生クラスへのポインタ、右?

だから、私は以前に派生クラス型

void (Test test) 
{ 
    vector<Base*> objects = test.base_vector; 
} 

QUESTION 2のオブジェクトへのポインタで満たされていた既存のテストクラスから、このベクトルにアクセスし、今言うことができます:それは可能ですの型名を推定します派生オブジェクト、ポインターは? (派生クラスはテンプレートです)

または派生クラスで定義された関数()を呼び出すことはできますか?派生クラスの型については気にしませんか?

もしそうでなければ、これらのクラスの最小限の変更は何ですか?

+0

あなたの問題はもう少し説明できますか? 「派生クラスの種類を推論する方法はありますか?」は本当に意味をなさない。 – Qix

+3

"明らかに、このクラスはDerivedクラスも受け入れます" ...オブジェクトのスライスについては、 'Derived 'を' Base'のベクトルに配置すると期待した効果が得られない可能性があります。 – user463035818

+0

多相ベクトル派生クラスのオブジェクトでは)オブジェクトではなくポインタを含む必要があります。 –

答えて

1

問題を正しく理解していれば、仮想関数を使用する必要があります。オブジェクト上の関数を呼び出して、正しい型の関数が呼び出されていることを確認するには、functionを仮想化します。だからあなたはBaseクラスでそれを宣言する必要があります。また、ベクトルをBaseへのポインタのベクトルにする必要があります。そして、ベクトルのオブジェクト上でfunctionを呼び出すと、オブジェクトが実際に派生型であれば派生クラスから関数を呼び出します。

オブジェクトの種類を調べる必要はありません。仮想関数を呼び出すだけで、オブジェクトの種類がわかり、正しい関数を呼び出すことができます。

+0

しかし、基本クラスを変更できない場合はどうすればいいですか? –

1

私はあなたが望むものを理解しているかどうかはわかりませんが、あなたのコードのこの変更はあなたのために機能しますか?こうすることで、型を全く推論する必要はなく、仮想型のtype()メソッドを取得することもできます。これは、すべての型に対して固有の値を持つ列挙型を返します。この問題への

#include <memory> 
#include <string> 
#include <vector> 
#include <iostream> 

class Base { 
public: 
    virtual void function() = 0; 
    virtual ~Base() = default; 
}; 

template <class T> class Derived : public Base { 
public: 
    void function() override { 
     // to produce some output 
     std::cout << typeid(T).name() << std::endl; 
    } 
}; 

class Test { 
public: 
    std::vector<std::unique_ptr<Base>> objects; 
}; 

int main() { 
    Test test; 
    test.objects.push_back(std::make_unique<Derived<int>>()); 
    test.objects.push_back(std::make_unique<Derived<std::string>>()); 
    for (auto &ptr : test.objects) { 
     ptr->function(); 
    } 
} 

代替ソリューションは、たとえばboost::variant、またはSTD ::実験::バリアント(あなたの標準ライブラリは既に1を持っている場合)のために、variantクラスを使用することです。この方法で、Baseクラスにvirtual function()を持つ必要はありません。 boost::variantにはwhich()メソッドがあり、これはあなたと型リストの型の整数値を返します。例:

#include <boost/variant.hpp> 
#include <iostream> 
#include <memory> 
#include <string> 
#include <vector> 

class Base { 
public: 
    virtual ~Base() = default; 
}; 

template <class T> class Derived : public Base { 
public: 
    void function() { 
    std::cout << typeid(T).name() << std::endl; 
    } 
}; 

class Test { 
public: 
    std::vector<boost::variant<Derived<int>, Derived<std::string>>> objects; 
}; 

int main() { 
    Test test; 
    test.objects.push_back(Derived<int>()); 
    test.objects.push_back(Derived<std::string>()); 
    for (auto& obj : test.objects) { 
    switch (obj.which()) { 
     case 0: 
     boost::get<Derived<int>>(obj).function(); 
     break; 
     case 1: 
     boost::get<Derived<std::string>>(obj).function(); 
     break; 
    } 
    } 
    return 0; 
} 
+0

最初の解決策は非常に合法です!しかし、もしBaseクラスがそれほど充実していて、それに何も変更を加えることができないのであれば? –

+0

@AlexShirokovまあ、dynamic_castでDerived にアップキャストしようとすることができます: 'if(auto derived = dynamic_cast >(ptr.get())){ptr-> function(); } '。オブジェクトが互換性のない型(すなわち、Derived またはそれから派生したものではない)の場合、nullptrを返します。ただし、このようなコードはすぐに乱雑になります。基本クラスに関数を追加するだけでははるかにクリーンです。 –

+1

@AlexShirokov誰かがベースクラスへのポインタをどこかで使用している場合、これによって、彼は基本クラスインタフェースが提供するものを正確に望んでいると述べています。彼が何かを追加したい場合、型を返す関数のように、基本クラスは彼が使用したいものではありません。それを回避するためにトリックを使用することは、コードのにおいです。間にあるクラスを作成し、適切なインタフェースを提供することで、サブクラスが導出され、ポインタがどのタイプのものであるかが分かります。もちろん、どんな場合でも、そのようなことは現実的な状況で決定されるべきです。 – Aziuth