如何区分派生类C ++的对象

时间:2015-11-22 01:29:49

标签: c++ inheritance

请看以下代码:

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()工作?

7 个答案:

答案 0 :(得分:8)

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

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

这不会帮助您在没有演员的情况下访问特定于BC的功能。解决此问题的一种方法是将BC虚拟成员函数提供给A,并通过{{1}进行调用} - AB的类型引用。

答案 1 :(得分:3)

不是没有调用未定义的行为。

问题是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(); } ,则可能。例如;

A

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

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

答案 2 :(得分:2)

使用指针的另一种方法是使用std::reference_wrapper s和多态类的向量。下面的小例子:

#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

答案 3 :(得分:2)

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

来自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();  

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

答案 4 :(得分:0)

如果您确定他们是B和C的实例,请使用演员:

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

您可以使用“类型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;
}