从派生的 class c++ 获取变量

Obtain variable from derived class c++

只有当 class 是特定派生的 class 时,我才会做某事。那就是我有:

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;} 
    }
}

我不希望 class B 有任何等同于 运行() 的东西,我也不希望 class A 有等同于 lala 的东西。

我知道 Fortran 有一个解决方法

select type ( x => var )
class is ( A )
    x.run()
end select

但我不确定我在 C++ 中的选项是什么。

谢谢

您可以使用 dynamic_cast 检查基础 class 指针是否可转换为派生实例。

另一种选择是使用 returns class 的 typeinfo 的虚函数,然后使用该信息将指针转换为可转换类型。根据 dynamic_cast 的实施方式,此 可能 性能更高。因此,如果您想尝试查看此方法在您的平台上是否更快,则可以使用它。

正如@Jarod42 指出的那样,您需要有一个虚函数,在这种情况下是析构函数,dynamic_cast 才能工作。此外,您只需要一个虚拟析构函数来避免删除实例时出现未定义的行为。

例子

#include <iostream>
#include <string>
#include <vector>
#include <typeinfo>

struct A {
    virtual ~A() {

    }

    virtual const std::type_info& getTypeInfo() const {
        return typeid(A);
    }
};

struct B : public A {
    virtual const std::type_info& getTypeInfo() const override {
        return typeid(B);
    }
};

struct C : public A {
    virtual const std::type_info& getTypeInfo() const override {
        return typeid(C);
    }
};



int main()
{
    std::vector<A*> data;
    data.push_back(new A);
    data.push_back(new B);
    data.push_back(new C);

    for (auto& val : data) {
        if (val->getTypeInfo() == typeid(A)) {
            std::cout << "A";
        }
        else if (val->getTypeInfo() == typeid(B)) {
            std::cout << "B";
        }
        else if (val->getTypeInfo() == typeid(C)) {
            std::cout << "C";
        }
        std::cout << std::endl;
    }

    for (auto& val : data) {
        delete val;
    }
}

您正在寻找 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

这个问题的现代 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
}

作为一个不错的奖励,这个解决方案优雅地摆脱了动态分配(不再有内存泄漏,不需要智能指针)。

我有两个想法....

为什么不使用一个共享方法 return 作为一个值来提供关于它是 A 还是 B 的上下文?例如,如果预计 lala return 的值仅为 0 或更大,则您可以使用 void 运行() 而不是 int 运行() 和 return -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 永远不会期望 return 特定范围的值时才有效。

您还可以在创建 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...,它就不再值得了,但对于只有两个派生的 classes 来说,还算不错。

我会基于这样一个事实来证明这一点:用户已经不得不查看派生的 classes 以了解为什么他们的行为如此不同,因为它们是从相同的 class 派生的。我实际上会研究 A 和 B 基于它们的接口从 X 派生是否有意义。

我也不会推荐 dynamic_cast(ing) 而不通知某人这是更危险的演员表演之一,通常不推荐。