我希望只有在类是特定的派生类时才会做某事。那就是我:
class X{
int id;
}
class A: public X{
void run();
}
class B: public X{
int lala;
}
我想在以下方面做点什么:
main(){
vector<X *> types;
types.push_back(new A);
types.push_back(new B);
int var = 0;
for(int i = 0; i<types.size(); i++){
if(types[i].isType(A)) {types[i].run();}
}
for(int i = 0; i<types.size(); i++){
if(types[i].isType(B)) {var = lala;}
}
}
我不希望B类有任何等同于run()的东西,也不希望A类具有等同于lala的东西。
我知道fortran有一个解决方法
select type ( x => var )
class is ( A )
x.run()
end select
但我不确定我在C ++中的选择是什么。
由于
答案 0 :(得分:5)
您正在寻找dynamic_cast
。
#include <vector>
using namespace std;
class X {
public:
int id;
virtual ~X() = default;
};
class A : public X {
public:
void run() {}
};
class B : public X {
public:
int lala;
};
main(){
vector<X *> types;
types.push_back(new A);
types.push_back(new B);
int var = 0;
for(int i = 0; i<types.size(); i++){
if (auto ta = dynamic_cast<A *>(types[i])) {
ta->run();
}
}
for(int i = 0; i<types.size(); i++){
if (auto tb = dynamic_cast<B *>(types[i])) {
var = tb->lala;
}
}
}
另请参阅此处的操作:https://onlinegdb.com/B1d29P5if。
我必须解决代码中的一些其他问题。由于它们不是您问题的一部分,我不会在此澄清,但欢迎您询问是否有不清楚的事情。
编辑:上面的解决方案有内存泄漏,我没有解决,因为问题不需要。为了完整起见,这里是固定内存泄漏的主要功能(https://onlinegdb.com/ByeOmu9iz):int main() {
vector<unique_ptr<X>> types;
types.emplace_back(new A);
types.emplace_back(new B);
int var = 0;
for(int i = 0; i < types.size(); ++i) {
if (auto ta = dynamic_cast<A *>(types[i].get())) {
ta->run();
}
}
for(int i = 0; i < types.size(); ++i) {
if (auto tb = dynamic_cast<B *>(types[i].get())) {
var = tb->lala;
}
}
}
请注意,这是一个C ++ 11解决方案。
如果您正在使用更旧的编译器,则必须像原始解决方案一样继续使用普通指针,并通过在向量的每个元素上调用delete
来手动释放内存。 。 (并且希望在你达到那一步之前没有任何事情会引发异常。)
您还必须将auto ta
替换为A* ta
,将auto tb
替换为B* tb
。
答案 1 :(得分:4)
解决此问题的现代C ++ 17解决方案是使用变体向量,即std::vector<std::variant<A, B>>
。你需要一个现代的编译器。
以下是基于std::variant
documentation:
#include <vector>
#include <variant>
#include <iostream>
class X {
int id;
};
class A: public X {
public:
void run() {
std::cout << "run\n"; // just for demonstration purposes
}
};
class B: public X {
public:
B(int lala) : lala(lala) {} // just for demonstration purposes
int lala;
};
int main() {
std::vector<std::variant<A, B>> types;
types.push_back(A()); // no more new!
types.push_back(B(123)); // no more new!
int var = 0;
for (auto&& type : types) {
std::visit([&](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, A>) {
arg.run();
} else {
var = arg.lala;
}
}, type);
}
std::cout << var << '\n'; // just for demonstration purposes
}
作为一个很好的奖励,这个解决方案优雅地摆脱了动态分配(没有更多的内存泄漏,没有必要的智能指针)。
答案 2 :(得分:0)
您可以使用dynamic_cast
检查基类指针是否可转换为派生实例。
另一种选择是拥有一个返回类的typeinfo
的虚函数,从而使用该信息将指针转换为可转换类型。根据{{1}}的实施方式,可以更高效。因此,如果您想尝试查看此方法在您的平台上是否更快,则可以使用此方法。
正如@ Jarod42所指出的那样,你需要有一个虚函数,在这种情况下是析构函数,dynamic_cast
才能工作。此外,在删除实例时,您只需要一个虚拟的析构函数来避免未定义的行为。
实施例
dynamic_cast
答案 3 :(得分:0)
我有两个想法......
为什么没有一个共享方法返回一个值,该值给出了关于它是A还是B的上下文?例如,如果期望lala仅返回值0或更大,则可以使用void run()而不是int run()并始终返回-1。
class X {
int id;
virtual int run() = 0; //Assuming X isn't meant to be instantiated
}
class A: public X {
// Return -1 to differentiate between As and Bs
int run() { return -1; }
}
class B: public X {
int lala;
int run() { return lala;}
}
然后你......
main(){
vector<X *> types;
types.push_back(new A);
types.push_back(new B);
int var = 0, temp = 0;
for( int i = 0; i<types.size(); i++ ) {
if( (temp = types[i].run()) != -1 )
var = temp;
....
}
}
同样,只有lala永远不会期望返回特定范围的值时才有效。
您还可以在创建A或B时隐藏X中的信息,以跟踪您拥有的内容。
class X {
int id;
bool isA;
}
class A: public X {
A() : isA(true) { };
void run();
}
class B: public X {
B() : isA(false) { } ;
int lala;
}
然后你......
main(){
vector<X *> types;
types.push_back(new A);
types.push_back(new B);
int var = 0;
for( int i = 0; i<types.size(); i++ ) {
if( types[i].isA == true ) {
types[i].run();
}
else {
var = types[i].lala;
}
}
当然,如果你希望添加C,D,E,......它将不再值得,但对于只有两个派生类,它并不是那么糟糕。
我基于这样的事实来证明这一点,即用户已经必须查看派生类,以了解为什么它们从同一个类派生的行为如此不同。实际上,我会根据它们的界面来研究A和B从X派生是否有意义。
我也不推荐使用dynamic_cast(ing)而不通知某人它是一个更危险的演员表演,通常不推荐。