繁体   English   中英

如何区分派生类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是基础课。 BC是派生类。 我想设置一个向量来保存BC ,并使用向量a来保存A 但是,由于a是包含A对象的向量,因此我无法访问BC方法。 有没有让a[0].getbb()a[1].getcc()工作?

A向量不能保持B s或C s,因为它按值存储A ,导致存储BC时的对象切片 特别是,这意味着当您存储B ,只存储aa ; bb被切掉了。

为了存储没有切片的子类,使用指针容器 - 最好是智能指针。

这不会帮助您在没有强制转换的情况下访问特定于BC功能。 解决此问题的一种方法是将BC的功能的虚拟成员函数赋予A ,并通过A或类型的BC引用进行调用。

并非没有调用未定义的行为。

问题是a.push_back(b)a.push_back(c)不会将对象bc附加到向量。 它们创建的A实例只包含“ A部分”。 这称为对象切片。

因此,向量中没有类型B对象,也没有类型C对象。

你强迫这个问题,并通过做类似的事情来编译你的代码

 static_cast<B &>(a[0]).getbb();

但这只是具有未定义的行为,因为当它真的是A类时,它将a[0]视为B类。 这是一个非常糟糕的主意。 虽然它(可能)会编译,但它可以做任何事情 - 可能不是你所期望的。

如果你的向量包含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();
  }

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

换句话说,你的设计被破坏了,所以修复它以便它不会以某种方式要求你将对象变形为不同的类型。

使用指针的另一种方法是使用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();
}

住在Coliru

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

从CPP参考:

dynamic_cast < new_type > ( expression )

...如果强制转换成功,dynamic_cast将返回new_type类型的值。 如果转换失败并且new_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();  

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

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

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

好的,你也可以创建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释放对象。

您可以使用“类型ID”:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM