请看以下代码:
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()
工作?
答案 0 :(得分:8)
A
的向量无法保留B
或C
,因为它按值存储A
,导致对象切片存储B
或C
时。特别是,这意味着当您存储B
时,只会存储aa
; bb
被切掉了。
为了存储没有切片的子类,使用指针容器 - 最好是智能指针。
这不会帮助您在没有演员的情况下访问特定于B
或C
的功能。解决此问题的一种方法是将B
和C
的虚拟成员函数提供给A
,并通过{{1}进行调用} - A
或B
的类型引用。
答案 1 :(得分:3)
不是没有调用未定义的行为。
问题是a.push_back(b)
和a.push_back(c)
不会将对象b
和c
附加到向量。他们创建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();
}
答案 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
中使用虚方法,并在B
和C
中覆盖它。但是(希望)这会回答标题中所述的完全问题。
答案 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;
}