将派生类类型转换为基类

时间:2016-05-31 19:48:42

标签: c++ c++11

编辑2:

以前的解决方案(“编辑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;
}

编辑1:

虽然Ryan的解决方案还不错,但我进一步阅读了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>>,一切都没问题,除了我松开类类型并且无法退回的部分。

我决定使用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>>并存储我的所有类,并在需要时可以转换回原始类型。

虽然我的基类将有一些其他虚拟方法,我将使用它而不是强制转换为派生类型,但会有一些例外。我将需要强制转换为派生类以使用某些特定方法,但我需要以某种方式知道派生类类型才能转换为它。我想知道是否有更简单,更清洁的方法吗?

2 个答案:

答案 0 :(得分: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();

通过这种方式,您的课程不必了解自己的实施情况。

答案 1 :(得分: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);
} 
相关问题