如何区分derived 类 C++的对象

How to distinguish objects of derived classes C++

看下面的代码:

class A
{
protected:
    int aa = 1;
};

class B : public A
{
private:
    int bb = 2;
public:
    int getbb() { return bb; }
};

class C : public A
{
private:
    int cc = 3;
public:
    int getcc() { return cc; }
};

int main()
{
    std::vector<A> a;
    B b;
    C c;
    a.push_back(b);
    a.push_back(c);

    a[0].getbb(); //getbb() unaccessible;
    a[1].getcc(); //getcc() unaccessible;
}

A 是基础class。 BC 是派生的 classes。我想设置一个向量来保存 BC,并使用向量 a 来保存 A。但是,由于 a 是一个包含 A 对象的向量,我无法访问 BC 中的方法。有没有办法让 a[0].getbb()a[1].getcc() 工作?

如果您确定它们是 B 和 C 的实例,请使用转换:

static_cast<B>(a[0]).getbb();
static_cast<C>(a[1]).getcc();

您的 A 向量无法保存 BC,因为它按值存储 A,导致 存储 BC 时的对象切片 。特别是,这意味着当您存储 B 时,只会存储 aabb 被切掉了。

为了在不切片的情况下存储子类,请使用指针容器 - 最好是 smart 指针。

这不会帮助您在没有转换的情况下访问特定于 BC 的功能。解决这个问题的一种方法是为 BC 的功能提供 virtual 成员函数给 A,并通过A 类型的引用 BC.

好的,你也可以创建一个向量 A*:

std::vector<A*> as;
as.push_back(new B);
as.push_back(new C);
B* b = (B*) as[0];
b->getbb();
c->getcc();

现在您只需要记住使用 delete 释放对象即可。

必须调用未定义的行为。

问题是 a.push_back(b)a.push_back(c) 没有将对象 bc 附加到矢量。他们创建仅包含“A 部分”的 A 实例。这称为对象切片。

所以向量中没有 B 类型的对象,也没有 C 类型的对象。

你强制这个问题并通过做类似

的事情让你的代码编译
 static_cast<B &>(a[0]).getbb();

但这只是有未定义的行为,因为它将 a[0] 视为 B 类型,而实际上它是 A 类型。这使它成为一个非常糟糕的主意。虽然它(可能)会编译,但它可以做任何事情——而且可能不是你所期望的。

如果您的矢量包含 A * 而不是 A,则有可能。例如;

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

       B* b = dynamic_cast<B *>(a[0]);
       if (b)    // if a[0] actually points at a B ....
          b->getbb();
       else
          complain_bitterly();

       C *c = dynamic_cast<C *>(a[1]);
       if (c)
          c->getcc();
       else
          complain_bitterly();
  }

当然,这样做也有实用的活板门——比如要求 class A 至少有一个 virtual member。最好使用多态基础并覆盖虚函数。

换句话说,您的设计已损坏,因此请修复它,这样它就不会以某种方式要求您将对象变形为不同的类型。

您可以使用 "Type IDs":

class A {
    // ...
    virtual int getTypeID() { return 0; }
}
class B {
    // ...
    virtual int getTypeID() { return 1; }
}
// analogically for C

它是虚拟的,但在 A 的原型中

现在使用:

switch(a.getTypeID()) {
    case 0:
        // It's normal A
        break;
    case 1:
        // It's B
        // ...
        break;
    case 2:
        // It's C
        // ...
        break;
}

使用指针的替代方法是使用 std::reference_wrapper 和多态 类 的向量。下面的小例子:

#include <functional> // for std::reference_wrapper
#include <iostream>
#include <vector>

class A
{
public:
    virtual void printme()
    {
        std::cout << "A" << std::endl;
    }
    virtual ~A() = default;
};

class B: public A
{
public:
    void printme() override
    {
        std::cout << "B" << std::endl;
    }
};

class C: public A
{
public:
    void printme() override
    {
        std::cout << "C" << std::endl;
    }
};

int main()
{
    std::vector<std::reference_wrapper<A>> a;
    B b;
    C c;

    a.emplace_back(b);
    a.emplace_back(c);

    a[0].get().printme(); // need to "get()" the raw reference
    a[1].get().printme();
}

Live on Coliru

根据 cpp 参考资料,似乎有一种方法可以通过使用 dynamic_cast 来实现。您首先需要使向量成为 指向 class A 的指针 的向量。然后在访问任何元素时,您可以通过检查 dynamic_cast 运算符的结果来检查它是否是 B*(或 C*)。

来自 CPP 参考:

dynamic_cast < new_type > ( expression )

... If the cast is successful, dynamic_cast returns a value of type new_type. If the cast fails and new_type is a pointer type, it returns a null pointer of that type...

因此,您可以这样做:

std::vector<A*> a;
B b;
C c;
a.push_back(&b);
a.push_back(&c);
...
int i = something;
B* pB = dynamic_cast<B*>(a[i]); if(pB != nullptr) pb->getbb();
C* pC = dynamic_cast<C*>(a[i]); if(pC != nullptr) pC->getcc();  

p.s:虽然它作为设计方法是非常值得怀疑的。推荐的 OOP 方法当然是在基础 class A 中使用虚拟方法并在 BC 中覆盖它。但是(希望)这回答了标题中所述的确切问题。