2016-05-31 11 views
2

基本クラスにキャストした後、EDIT 2:保存派生クラス型


前の溶液(「EDIT 1」)switchでは動作しないだろうが、私は実際にそれがしたかったです。私はconstexpr compile time counterを見つけたので、私はswitchを使うことができた。 Visual Studio 2015 IDEはコンパイル時のカウンタ値を(まだ)constexprと判断できず、すべて同じであると思っていますが、うまくコンパイルできます。私の更新ソリューションは、以下見つけることができます:

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

namespace compileTimeCounter { 
    template<int N> 
    struct flag { 
     friend constexpr int adl_flag(flag<N>); 
    }; 

    template<int N> 
    struct writer { 
     friend constexpr int adl_flag(flag<N>) { 
      return N; 
     } 

     static constexpr int value = N; 
    }; 

    template<int N, class = char[noexcept(adl_flag(flag<N>())) ? +1 : -1]> 
    int constexpr reader(int, flag<N>) { 
     return N; 
    } 

    template<int N> 
    int constexpr reader(float, flag<N>, int R = reader(0, flag<N - 1>())) { 
     return R; 
    } 

    int constexpr reader(float, flag<0>) { 
     return 0; 
    } 

    template<int N = 1, int C = reader(0, flag<32>())> 
    int constexpr next(int R = writer<C + N>::value) { 
     return R; 
    } 
} 

class objectWrapper { 
public: 
    virtual size_t getType() const noexcept = 0; 
}; 

template<typename T> 
class typeWrapper : public objectWrapper { 
public: 
    static constexpr size_t type = compileTimeCounter::next(); 
    size_t getType() const noexcept { return this->type; } 
}; 

class classA : public typeWrapper<classA> { 
public: 
    classA() { std::cout << "classA ctor" << std::endl; } 
    ~classA() { std::cout << "classA dtor" << std::endl; } 
    void methodA() { std::cout << "methodA called" << std::endl; } 
}; 

class classB : public typeWrapper<classB> { 
public: 
    classB() { std::cout << "classB ctor" << std::endl; } 
    ~classB() { std::cout << "classB dtor" << std::endl; } 
    void methodB() { std::cout << "methodB called" << std::endl; } 
}; 

class classC : public typeWrapper<classC> { 
public: 
    classC() { std::cout << "classC ctor" << std::endl; } 
    ~classC() { std::cout << "classC dtor" << std::endl; } 
    void methodC() { std::cout << "methodC called" << std::endl; } 
}; 

int main() { 
    std::vector<std::shared_ptr<objectWrapper>> objects1, objects2; 
    objects1.push_back(std::make_shared<classA>()); 
    objects1.push_back(std::make_shared<classB>()); 
    objects1.push_back(std::make_shared<classC>()); 

    objects2 = objects1; 

    switch (objects2[0]->getType()) { 
     case classA::type: 
      reinterpret_cast<classA*>(objects2[0].get())->methodA(); 
      break; 
     case classB::type: 
      reinterpret_cast<classB*>(objects2[0].get())->methodB(); 
      break; 
     case classC::type: 
      reinterpret_cast<classC*>(objects2[0].get())->methodC(); 
      break; 
    } 

    objects2.~vector(); 
    std::cout << "objects2 destroyed" << std::endl; 
    objects1.~vector(); 
    std::cout << "objects1 destroyed" << std::endl; 

    std::cin.get(); 
    return 0; 
} 

EDIT 1:


ライアンのソリューションは悪くないですが、私はさらに、約dynamic_castを読み、それが遅くなる可能性があることが分かりました特定の状況では一方、私はskypjackのソリューションが本当に好きで、私のコードを彼と一緒に更新しました(派生クラスから静的カウンタを隠すように少し修正しました)。

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

template<typename T> 
struct typeWrapper; 

struct objectWrapper { 
    template<typename T> 
    friend struct typeWrapper; 
private: 
    static size_t typeCounter; 
public: 
    virtual size_t getType() const noexcept = 0; 
}; 

size_t objectWrapper::typeCounter = 0; 

template<typename T> 
struct typeWrapper : objectWrapper { 
    static const size_t type; 
    size_t getType() const noexcept { return this->type; } 
}; 

template<typename T> 
const size_t typeWrapper<T>::type = objectWrapper::typeCounter++; 

class classA : public typeWrapper<classA> { 
public: 
    classA() { std::cout << "classA ctor" << std::endl; } 
    ~classA() { std::cout << "classA dtor" << std::endl; } 
    void methodA() { std::cout << "methodA called" << std::endl; } 
}; 

class classB : public typeWrapper<classB> { 
public: 
    classB() { std::cout << "classB ctor" << std::endl; } 
    ~classB() { std::cout << "classB dtor" << std::endl; } 
    void methodB() { std::cout << "methodB called" << std::endl; } 
}; 

class classC : public typeWrapper<classC> { 
public: 
    classC() { std::cout << "classC ctor" << std::endl; } 
    ~classC() { std::cout << "classC dtor" << std::endl; } 
    void methodC() { std::cout << "methodC called" << std::endl; } 
}; 

int main() { 
    std::vector<std::shared_ptr<objectWrapper>> objects1, objects2; 
    objects1.push_back(std::make_shared<classA>()); 
    objects1.push_back(std::make_shared<classB>()); 
    objects1.push_back(std::make_shared<classC>()); 

    objects2 = objects1; 

    if (objects2[0]->getType() == classA::type) 
     reinterpret_cast<classA*>(objects2[0].get())->methodA(); 
    else if (objects2[0]->getType() == classB::type) 
     reinterpret_cast<classB*>(objects2[0].get())->methodB(); 
    else if (objects2[0]->getType() == classC::type) 
     reinterpret_cast<classC*>(objects2[0].get())->methodC(); 

    objects2.~vector(); 
    std::cout << "objects2 destroyed" << std::endl; 
    objects1.~vector(); 
    std::cout << "objects1 destroyed" << std::endl; 

    std::cin.get(); 
    return 0; 
} 

私はshared_ptrベクトル内の異なるクラスのオブジェクトを格納するために、いくつかの方法を探しています。さて、私はそれらを内部に保管すると、vector<shared_ptr<void>>としましょう。私はクラスのタイプが緩んでいて、キャストできない部分を除いて、すべてがOKです。

enumと基本クラス(objectWrapper)を使用して、手動でクラスタイプを保存することに決めました。例を以下に示します。

#include <iostream> 
#include <memory> 
#include <vector> 
#include <inttypes.h> 

enum class objectType : uint8_t { 
    classA, 
    classB, 
    classC 
}; 

class objectWrapper { 
protected: 
    objectType type; 
    objectWrapper(objectType type) : type(type) {} 
public: 
    virtual objectType getObjectType() { 
     return this->type; 
    } 
}; 

class classA : public objectWrapper { 
public: 
    classA() : objectWrapper(objectType::classA) { 
     std::cout << "classA ctor" << std::endl; 
    } 
    ~classA() { std::cout << "classA dtor" << std::endl; } 
    void methodA() { std::cout << "methodA called" << std::endl; } 
}; 

class classB : public objectWrapper { 
public: 
    classB() : objectWrapper(objectType::classB) { 
     std::cout << "classB ctor" << std::endl; 
    } 
    ~classB() { std::cout << "classB dtor" << std::endl; } 
    void methodB() { std::cout << "methodB called" << std::endl; } 
}; 

class classC : public objectWrapper { 
public: 
    classC() : objectWrapper(objectType::classC) { 
     std::cout << "classC ctor" << std::endl; 
    } 
    ~classC() { std::cout << "classC dtor" << std::endl; } 
    void methodC() { std::cout << "methodC called" << std::endl; } 
}; 

int main() { 
    std::vector<std::shared_ptr<objectWrapper>> objects1, objects2; 
    objects1.push_back(std::make_shared<classA>()); 
    objects1.push_back(std::make_shared<classB>()); 
    objects1.push_back(std::make_shared<classC>()); 

    objects2 = objects1; 

    switch (objects2[0]->getObjectType()) { 
     case objectType::classA: 
      dynamic_cast<classA*>(objects2[0].get())->methodA(); 
      break; 
     case objectType::classB: 
      dynamic_cast<classB*>(objects2[0].get())->methodB(); 
      break; 
     case objectType::classC: 
      dynamic_cast<classC*>(objects2[0].get())->methodC(); 
      break; 
     default: 
      break; 
    } 

    objects2.~vector(); 
    std::cout << "objects2 destroyed" << std::endl; 
    objects1.~vector(); 
    std::cout << "objects1 destroyed" << std::endl; 

    std::cin.get(); 
    return 0; 
} 

私はvector<shared_ptr<objectWrapper>>を作成し、すべての私のクラスを保存し、必要なときに、私は、元の型にキャストバックすることができますすることができますこの方法。

私の基本クラスには、派生型にキャストする代わりに使用するいくつかの他の仮想メソッドがありますが、いくつかの例外があります。私はいくつかの特定のメソッドを使うために、派生クラスにキャストする必要がありますが、キャストする前に派生クラスの型を何とか知る必要があります。私はそれを行うためのより簡単でクリーンな方法があるのだろうかと思っていましたか?

答えて

1

別の解決策は、テンプレートとCRTPイディオムを使用することです:

#include<cassert> 

struct B { 
    static int cnt; 
    virtual int type() const noexcept = 0; 
}; 

int B::cnt = 0; 

template<typename T> 
struct D: B { 
    static const int family; 
    int type() const noexcept override { return family; } 
}; 

template<typename T> 
const int D<T>::family = B::cnt++; 

struct A: D<A> { }; 
struct C: D<C> { }; 

int main() { 
    B *a = new A; 
    B *c = new C; 
    assert(a->type() != c->type()); 
    assert(a->type() == A::family); 
} 
+0

まあ...何を言うべきか分からない...天才のアイデア!私は似たようなことを試しましたが、この解決策には至っていませんでした。さて、最後に 'delete'sを追加すれば完璧です。 – FrogTheFrog

3

最も単純な方法は、基本クラスを派生クラスに動的にキャストし、それが成功したかどうかをチェックすることです。たとえば、代わりにあなたのswitch文に方法objects2[0]->getObjectType()でswitch文を使用しての、あなたが置くことができる

classA* CA = dynamic_cast<classA*>(objects2[0].get()); 
if(CA) CA->methodA(); 

classB* CB = dynamic_cast<classB*>(objects2[0].get()); 
if(CB) CB->methodB(); 

classC* CC = dynamic_cast<classC*>(objects2[0].get()); 
if(CC) CC->methodC(); 

あなたのクラスは、独自の実装について知る必要はありませんこの方法です。

+0

はdynamic_cast'が実際に(でも、これを期待していなかった) '偽の鋳造にnullptr'を返す'知りませんでした。これは私に多くの新しい可能性を与えます。誰もより良い解決策を出さない場合は、後で回答を選択します。 – FrogTheFrog