[英]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
是基础课。 B
和C
是派生类。 我想设置一个向量来保存B
或C
,并使用向量a
来保存A
但是,由于a
是包含A
对象的向量,因此我无法访问B
和C
方法。 有没有让a[0].getbb()
和a[1].getcc()
工作?
A
向量不能保持B
s或C
s,因为它按值存储A
,导致存储B
或C
时的对象切片 。 特别是,这意味着当您存储B
,只存储aa
; bb
被切掉了。
为了存储没有切片的子类,使用指针容器 - 最好是智能指针。
这不会帮助您在没有强制转换的情况下访问特定于B
或C
功能。 解决此问题的一种方法是将B
和C
的功能的虚拟成员函数赋予A
,并通过A
或类型的B
或C
引用进行调用。
并非没有调用未定义的行为。
问题是a.push_back(b)
和a.push_back(c)
不会将对象b
和c
附加到向量。 它们创建的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();
}
根据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
使用虚方法并在B
和C
覆盖它。 但是(希望)这回答了标题中所述的确切问题。
如果您确定它们是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.