2017-02-20 3 views
1

In C++ Sie virtuelle Methoden verwenden können folgende Code Arbeit haben, wie Sie erwarten:C++ type casting Alternative zu virtuellen Methoden

#include <iostream> 
#include <string> 

class BaseClass { 
public: 
    virtual std::string class_name() const { return "Base Class"; } 
}; 

class FirstClass : public BaseClass { 
    int value = 1; 
public: 
    std::string class_name() const { return "FirstClass"; } 
}; 

class SecondClass : public BaseClass { 
    long long value = -1; 
public: 
    std::string class_name() const { return "SecondClass"; } 
}; 

int main() { 
    const int array_size = 5; 
    const bool in_first_mode = true; 

    void *data; 
    int sample_size; 
    if (in_first_mode) { 
     data = new FirstClass[array_size]; 
     sample_size = sizeof(FirstClass); 
    } else { 
     data = new SecondClass[array_size]; 
     sample_size = sizeof(SecondClass); 
    } 

    // this is class-independent code 
    for (int index = 0; index < array_size; ++index) { 
     BaseClass *pointer = static_cast<BaseClass*>(data + index * sample_size); 
     std::cout << pointer->class_name() << std::endl; 
    } 

    return 0; 
} 

Dies sowohl für richtig in_first_mode = true und in_first_mode = false arbeiten. Also, im Grunde, wenn Sie Code schreiben wollen, der für beide Klassen funktioniert, können Sie einfach den Zeiger auf die BaseClass verwenden.

Aber was ist, wenn Sie bereits einen Datenpuffer angegeben haben, gefüllt mit Daten vom Typ TypeOne, TypeTwo, TypeThree oder TypeFour, und in Laufzeit kennen Sie diesen Typ, der in int type gespeichert ist. Problem ist, dass TypeOne, TypeTwo, TypeThree und TypeFour nicht von einer Basisklasse geerbt wurden. In meinem Fall sind das tatsächlich Strukturen aus der 3rd-Party-Bibliothek, die bereits eine kompilierte C-kompatible Bibliothek ist, so dass ich sie nicht modifizieren kann. Ich möchte etwas wie pointer aus dem obigen Beispiel bekommen, aber das Problem tritt auf, wenn man identifiziert, welcher C++ Typ diese pointer haben soll.

Es gibt eine elegantere Art Guss Alternative C++ Klassen-Wrapper für diese vier Typen zu machen (was oben etwas ähnlich dem Beispiel gibt) und pointer zu machen void * und Notwendigkeit

if (type == 1) { 
    TypeOne *type_one_pointer = static_cast<TypeOne*>(pointer); 
    // do something 
} else if (type == 2) { 
/* ... */ 
} 

sein jeder Zeit verwende ich pointer?

+2

Sie haben verschiedene Methoden? Wenn nein - Sie können Vorlagen verwenden. Wenn ja - Sie sollten mit ihnen wie mit verschiedenen Klassen arbeiten. – ForEveR

+0

Speichern Sie Funktionszeiger in einem Array und verwenden Sie "type" als Index. – knivil

+0

@ForEveR, wie kann ich Vorlagen dafür verwenden? – Dmitry

Antwort

2

Wenn die Klassen nicht verwandt sind, können Sie sie in einem std::variant speichern (oder Boost.Variant verwenden wenn Ihr Compiler nicht C++ 17-konform ist) und auf den Wert mit einem Besucher zugreifen. Dies ist flexibler als Vorlagen, da Sie Typen mit einer anderen Schnittstelle in den Variantentyp einbeziehen können.

Zum Beispiel (ich habe nicht kompiliert diesen Code):

#include <iostream> 
#include <string> 
#include <variant> 
#include <vector> 

struct TypeOne { 
    std::string class_name() const { return "Type one"; } 
}; 

struct TypeTwo { 
    int value = 1; 
    std::string class_name() const { return "Type two"; } 
}; 

struct TypeThree { 
    long long value = -1; 
    // note the different function signature 
    static std::string class_name() { return "Type three"; } 
}; 

struct TypeFour { 
    std::string getMyClassName() const { return "Type four"; } 
}; 

struct Visitor { 
    template <class T> 
    void operator()(T&& value) const { 
     std::cout << value.class_name() << std::endl; 
    } 

    // special case   
    void operator()(const TypeFour& value) const { 
     std::cout << value.getMyClassName() << std::endl; 
    } 
}; 

int main() { 
    typedef std::variant<TypeOne, TypeTwo, TypeThree, TypeFour> Variant; 
    std::vector<Variant> values; 
    values.emplace_back(TypeOne{}); 
    values.emplace_back(TypeTwo{}); 
    values.emplace_back(TypeThree{}); 
    values.emplace_back(TypeFour{}); 

    for (const auto& var : values) { 
     std::visit(Visitor{}, var); 
    } 
} 
+0

Aber ich denke, OP will 'std :: variant ' – Jarod42

+0

Dies wird genau ich wollte - eine elegante Lösung für dieses Problem. Wenn diese Features auf Compilern landen werden (GCC 7, Clang 4.0, MSVC 2017 - alle werden bald veröffentlicht).Fürs Erste werde ich eine Template-Lösung verwenden, da ich nur dafür keinen Boost haben möchte. – Dmitry

0

Dank @ForEveR finde ich die Lösung. Ich muss Vorlagen verwenden.

Es bedeutet, dass man tun kann, so dass, wenn in dem obigen Beispiel und Firstclass Second hätte keine Baseclass haben:

#include <iostream> 
#include <string> 

class FirstClass { 
    int value = 1; 
public: 
    std::string class_name() const { return "FirstClass"; } 
}; 

class SecondClass { 
    long long value = -1; 
public: 
    std::string class_name() const { return "SecondClass"; } 
}; 

template <typename T> 
void do_my_stuff(void* void_pointer) { 
    T *pointer = static_cast<T*>(void_pointer); 
    std::cout << pointer->class_name() << std::endl; 
} 

int main() { 
    const int array_size = 5; 
    const bool in_first_mode = true; 

    void *data; 
    int sample_size; 
    if (in_first_mode) { 
     data = new FirstClass[array_size]; 
     sample_size = sizeof(FirstClass); 
    } else { 
     data = new SecondClass[array_size]; 
     sample_size = sizeof(SecondClass); 
    } 

    for (int index = 0; index < array_size; ++index) { 
     if (in_first_mode) { 
      do_my_stuff<FirstClass>(data + index * sample_size); 
     } else { 
      do_my_stuff<SecondClass>(data + index * sample_size); 
     } 
    } 

    return 0; 
} 
+0

Zeigerarithmetik wieder ... – JHBonarius