编辑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>>
并存储我的所有类,并在需要时可以转换回原始类型。
虽然我的基类将有一些其他虚拟方法,我将使用它而不是强制转换为派生类型,但会有一些例外。我将需要强制转换为派生类以使用某些特定方法,但我需要以某种方式知道派生类类型才能转换为它。我想知道是否有更简单,更清洁的方法吗?
答案 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);
}