访问存储在向量C++中的结构的多态成员
Accessing polymorphic members of a struct that is stored in a vector C++
我有一个结构a和结构B(B继承自a(。有没有一种方法可以创建std::vector,它具有模板类型a,但可以接受B类型的结构,并且当迭代通过时,我可以访问结构B专有的成员(显然要检查以确保它是B类型(。
您可以将对象存储为指针,并使用dynamic_cast
检查下转换。
#include <iostream>
#include <memory>
#include <vector>
struct A {
virtual ~A() = default;
};
struct B : public A {
void test() { std::cout << "I'm B(" << this << ")" << std::endl; }
};
int main() {
std::vector<std::unique_ptr<A>> elements;
elements.push_back(std::make_unique<A>());
elements.push_back(std::make_unique<B>());
for (const auto &e : elements) {
if (auto ptr = dynamic_cast<B *>(e.get())) {
ptr->test();
}
}
return 0;
}
Tarek的答案适用于使用动态内存分配的常见情况(如果sizeof(B)
明显大于sizeof(A)
,则通常更好(。我不想与这个答案竞争,只是想补充一些讨论点。在许多(但远非所有(问题域中,dynamic_cast
被认为是一种糟糕的做法,而不是在A
中添加virtual void test() { }
——请注意,函数体什么都不做——然后使B
的test
成为覆盖(即void test() override { ...existing body...}
(。这样,循环可以只说ptr->test()
,而不关心运行时类型(即它是否真的是B
对象(。如果"test
"操作对类的整个继承结构具有某种逻辑意义,那么这种方法更有意义,但目前在A
中没有什么值得测试的,特别是当您直接或通过B
添加从A
派生的C
类型时,它还希望从循环中调用test
函数:您不希望真的必须转到每个这样的循环并添加额外的dynamic_cast<>
测试。
只是为了找到一个更接近您对vector
的请求的替代方案,该CCD_19可以"接受B
类型的结构">(尽管不再是vector<A>
(,您可以使用std::variant
获得大致相同的结果,并将A
或B
直接存储在vector
管理的连续内存中,如果大小差异很小或内存使用情况无关紧要,但对象足够小,CPU缓存位置对性能很有用,则效果最好。
#include <vector>
#include <iostream>
#include <variant>
struct A {
virtual void f() const { /* do nothing */ }
};
struct B : A {
int i_;
B(int i) : i_{i} { }
void f() const override { std::cout << "Bn"; }
void g() const { std::cout << "B::g() " << i_ << 'n'; }
};
int main()
{
std::vector<std::variant<A, B>> v{ A{}, A{}, B{2}, A{}, B{7}, B{-4}, A{} };
for (const auto& x : v)
if (const auto* p = std::get_if<B>(&x))
p->g();
}
另外,不能简单地使用vector<A>
并用B
对象覆盖某些元素的原因是,以这种方式向编译器撒谎会产生未定义的行为。关于为什么这可能被该语言禁止的示例,请考虑对于正常的代码生成,编译器应该能够依赖于vector
只存储A
类型对象的编译时知识,例如,对任何dynamic_cast<B*>
的nullptr
返回(或从dynamic_cast<B&>
抛出(进行硬编码。当你使用运行时多态性时,你可能会认为应该禁止编译器不进行运行时检查,但事实恰恰相反——编译器优化器会非常努力地识别运行时类型已知的情况,并且可以避免虚拟调度,因为这避免了一点间接性和越界的函数调用,并且可以允许死代码消除(即,如果test()
什么都不做,则不生成用于对其的调用的任何代码(。
用B
s选择性地重写vector
中的A
对象的其他实际问题:-当vector
的析构函数被析构函数时,将调用错误的元素析构函数-如果有人将贝斯的数据成员添加到B
中,使得sizeof(B) > sizeof(A)
,则将new
放置到vector
中将覆盖以下对象的内存(或vector
的末尾(。
有一种东西被称为Copeland Virtual Constructor Idiom,其中对象的类型被更改为类似于operator new(&a) B{}
(尽管在这种情况下,它可以从构造函数operator new(this) B{}
中完成(,但你必须是一名语言和/或实现专家,才能知道它是否/何时可以安全使用。
- 访问存储在向量C++中的结构的多态成员
- C++多态数据结构
- 结构化绑定语法是否可以在多态 lambda 中使用
- Sean Parent:对于继承层次结构中的多态类型,具有可变对象是极端的例外
- C++标准是否允许复制任意多态数据结构?
- C++ Arduino的结构多态性
- Arduino中的C 结构多态性
- 用 msgpack 打包多态类结构
- 将静态访问者与静态多态性层次结构相耦合
- 基于PHP示例,与C 中数据结构一起工作的多态性示例
- 设计一个没有虚拟析构函数的多态类层次结构
- 在继承层次结构中将方法定义为虚拟方法一次,以使多态性发挥作用
- 具有多态性和不同数据结构的程序设计
- C++ 具有多父多态层次结构的 VTable 实现
- 在c++中,当多态结构不存在时,如何传递它们
- 是否可以为模板化的基类提供专门的类层次结构,但仍利用它们之间的多态性
- C++:在不违反SRP的情况下向多态类层次结构添加方法
- 如何对多态继承层次结构执行灵活的序列化
- 多态和通过带指针的结构体进行强制转换
- 语言律师-正在铸造具有公共基础和布局的非多态结构有效的C++