2016-05-31 11 views
2

EDIT 2 Gießen:Speichern abgeleiteten Klasse Typ, nachdem es auf Basisklasse


Vorherige Lösung („EDIT 1“) nicht mit switch arbeiten, aber ich wollte es wirklich. Nachdem ich etwas um Google gegraben habe, habe ich constexpr compile time counter entdeckt, was mir erlauben würde, switch zu verwenden. Visual Studio 2015 IDE ist nicht in der Lage, constexpr Kompilierzeit Zählerwerte (noch) zu bestimmen und denke, dass sie alle gleich sind, aber es kompiliert einfach gut.

#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; 
} 

1 EDIT:


Während Ryan-Lösung ist nicht schlecht, ich lese weiter über dynamic_cast und fand heraus, dass es langsam sein könnte meine aktualisierte Lösung finden Sie unten in bestimmten Situationen. Auf der anderen Seite mag ich skypjacks Lösung sehr und habe meinen Code mit seinem (leicht modifizierten, um statischen Zähler aus abgeleiteten Klassen zu verstecken) aktualisiert.

#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; 
} 

ich nach einer Möglichkeit suchen andere Klasse Objekte in shared_ptr Vektor zu speichern. Jetzt, wenn ich sie innen aufbewahre, sagen wir vector<shared_ptr<void>>, ist alles in Ordnung, außer für den Teil, wo ich meinen Klassentyp verliere und nicht zurückwerfen kann.

Ich habe beschlossen, den Klassentyp manuell zu speichern, indem enum und Basisklasse (objectWrapper) verwendet wird. Beispiel ist unten gezeigt.

#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; 
} 

So kann ich vector<shared_ptr<objectWrapper>> und speichern alle meine Klassen und bei Bedarf erstellen können, kann ich auf ihre ursprünglichen Arten zurückgeworfen.

Während meine Basisklasse über einige andere virtuelle Methoden verfügt, die ich anstelle des abgeleiteten Typs verwenden werde, gibt es einige Ausnahmen. Ich muss zur abgeleiteten Klasse zurückkehren, um einige spezifische Methoden zu verwenden, aber ich muss den abgeleiteten Klassentyp irgendwie kennen, bevor ich ihn umwandeln kann. Ich habe mich gefragt, ob es einen einfacheren und saubereren Weg gibt, es zu tun?

Antwort

1

Eine andere Lösung wäre, Vorlagen zu verwenden und die CRTP Idiom:

#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

Nun ... Ich weiß nicht was ich sagen soll ... Genius Idee! Ich habe etwas Ähnliches versucht, war aber dieser Lösung nicht einmal nahe.Nun, wenn Sie einfach 'delete' am Ende hinzufügen, wäre es perfekt. – FrogTheFrog

3

Der einfachste Weg wäre einfach, die Basisklasse dynamisch in die abgeleitete Klasse zu konvertieren und zu überprüfen, ob sie erfolgreich war.

Zum Beispiel, anstatt eine switch-Anweisung mit der Methode objects2[0]->getObjectType() in Ihrer switch-Anweisung zu verwenden, könnte man sagen:

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(); 

Auf diese Weise Ihre Klassen haben nicht über ihre eigene Implementierung kennen.

+0

Wusste nicht, 'dynamic_cast' würde tatsächlich zurückkehren' nullptr' auf falschen Casting (habe dies nicht einmal erwarten). Das gibt mir viele neue Möglichkeiten. Wird später Ihre Antwort wählen, wenn niemand eine bessere Lösung gibt. – FrogTheFrog

Verwandte Themen