从派生类c ++获取变量

时间:2018-04-10 16:34:12

标签: c++ class oop derived

我希望只有在类是特定的派生类时才会做某事。那就是我:

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 ++中的选择是什么。

由于

4 个答案:

答案 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)而不通知某人它是一个更危险的演员表演,通常不推荐。