如何区分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。 B
和 C
是派生的 classes。我想设置一个向量来保存 B
或 C
,并使用向量 a
来保存 A
。但是,由于 a
是一个包含 A
对象的向量,我无法访问 B
和 C
中的方法。有没有办法让 a[0].getbb()
和 a[1].getcc()
工作?
如果您确定它们是 B 和 C 的实例,请使用转换:
static_cast<B>(a[0]).getbb();
static_cast<C>(a[1]).getcc();
您的 A
向量无法保存 B
或 C
,因为它按值存储 A
,导致 存储 B
或 C
时的对象切片 。特别是,这意味着当您存储 B
时,只会存储 aa
; bb
被切掉了。
为了在不切片的情况下存储子类,请使用指针容器 - 最好是 smart 指针。
这不会帮助您在没有转换的情况下访问特定于 B
或 C
的功能。解决这个问题的一种方法是为 B
和 C
的功能提供 virtual 成员函数给 A
,并通过A
类型的引用 B
或 C
.
好的,你也可以创建一个向量 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)
没有将对象 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();
}
当然,这样做也有实用的活板门——比如要求 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();
}
根据 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
中使用虚拟方法并在 B
和 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。 B
和 C
是派生的 classes。我想设置一个向量来保存 B
或 C
,并使用向量 a
来保存 A
。但是,由于 a
是一个包含 A
对象的向量,我无法访问 B
和 C
中的方法。有没有办法让 a[0].getbb()
和 a[1].getcc()
工作?
如果您确定它们是 B 和 C 的实例,请使用转换:
static_cast<B>(a[0]).getbb();
static_cast<C>(a[1]).getcc();
您的 A
向量无法保存 B
或 C
,因为它按值存储 A
,导致 存储 B
或 C
时的对象切片 。特别是,这意味着当您存储 B
时,只会存储 aa
; bb
被切掉了。
为了在不切片的情况下存储子类,请使用指针容器 - 最好是 smart 指针。
这不会帮助您在没有转换的情况下访问特定于 B
或 C
的功能。解决这个问题的一种方法是为 B
和 C
的功能提供 virtual 成员函数给 A
,并通过A
类型的引用 B
或 C
.
好的,你也可以创建一个向量 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)
没有将对象 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();
}
当然,这样做也有实用的活板门——比如要求 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();
}
根据 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
中使用虚拟方法并在 B
和 C
中覆盖它。但是(希望)这回答了标题中所述的确切问题。