C++ 访问存储在向量C+中的结构的多态成员+;

C++ 访问存储在向量C+中的结构的多态成员+;,c++,polymorphism,C++,Polymorphism,我有一个结构a和结构B(B继承自a)。有没有一种方法可以创建std::vector,它的模板类型为a,但可以接受B类型的结构,当遍历时,我可以访问结构B专有的成员(显然是检查以确保它是B类型的)。您可以使用存储对象作为指针,并使用动态\u cast检查向下转换 #include <iostream> #include <memory> #include <vector> struct A { virtual ~A() = default; }; stru

我有一个结构a和结构B(B继承自a)。有没有一种方法可以创建std::vector,它的模板类型为a,但可以接受B类型的结构,当遍历时,我可以访问结构B专有的成员(显然是检查以确保它是B类型的)。

您可以使用存储对象作为指针,并使用
动态\u 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;
}
#包括
#包括
#包括
结构A{
virtual~A()=默认值;
};
结构B:公共A{

void test(){std::cout您可以使用存储对象作为指针,并使用
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;
}
#包括
#包括
#包括
结构A{
virtual~A()=默认值;
};
结构B:公共A{

void test(){std::coutTarek的答案适用于使用动态内存分配(如果
sizeof(B)
明显大于
sizeof(A)
,通常会更好)的常见情况。我不想与这个答案竞争,只是增加一些讨论点。在很多情况下(但远不是所有情况下)问题域将
dynamic_u u cast
而不是添加到
a
a
virtualvoid test(){}
-注意函数体什么都不做-然后使
B
test
重写(即
void test()重写{…现有体…}
)。这样,循环可以只说
ptr->test()
,而不关心运行时类型(即它是否实际上是
B
对象)。如果“
test
”操作对于类的整个继承权有某种逻辑意义,但是现在在
A
中没有什么值得测试的,特别是当您直接或通过
B
添加从
A
派生的
C
类型时,它还需要从循环调用
test
函数:您真的不需要您需要转到每个这样的循环并添加额外的
dynamic\u cast
测试

只是为了好玩,有一个替代方案恰好更接近您对
向量
的请求,它可以“接受
B
类型结构”(尽管不再是
向量
),您可以使用
std::variant
获得大致相同的结果,并将
A
B
直接存储在
矢量管理的连续内存中,如果大小差异很小或内存使用情况无关紧要,则效果最佳,但对象足够小,因此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 << "B\n"; }
    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();
}
#包括
#包括
#包括
结构A{
虚空f()常量{/*不执行任何操作*/}
};
结构B:A{
国际组织;
B(inti):i{i}{}
void f()const override{std::cout sizeof(A)
,placement
new
-将
B
对象插入
向量
将覆盖以下对象的内存(或关闭
向量


我听说有一个东西叫做Copeland虚拟构造函数习惯用法,其中对象的类型更改类似于
操作符new(&a)B{}
(尽管在这种情况下,它可以从构造函数
操作符new(this)B{}
完成),但您必须是语言和/或实现专家才能知道它是否/何时使用是安全的。

Tarek的答案适用于使用动态内存分配的常见情况(如果
sizeof(B)
明显大于
sizeof(a)
,则通常更好)。我不想与这个答案竞争,只是增加一些讨论点。在许多(但远不是所有)问题领域,将
dynamic\u cast
,而不是添加到
a
a
virtual void test(){}被认为是一种糟糕的做法
-注意函数体什么都不做-然后使
B
test
成为一个覆盖(即
void test()覆盖{…现有体…}
)。这样,循环就可以说
ptr->test()
,而不关心运行时类型(即它是否实际上是
B
对象)。如果“
测试”
,则此方法更有意义操作对于类的整个继承权有某种逻辑意义,但是现在在
A
中没有什么值得测试的,特别是当您直接或通过
B
添加从
A
派生的
C
类型时,它还需要从循环调用
test
函数:您真的不需要您需要转到每个这样的循环并添加额外的
dynamic\u cast
测试

只是为了好玩,有一个替代方案恰好更接近您对
向量
的请求,它可以“接受
B
类型结构”(尽管不再是
向量
),您可以使用
std::variant
获得大致相同的结果,并将
A
B
直接存储在
矢量管理的连续内存中,如果大小差异很小或内存使用情况无关紧要,则效果最佳,但对象足够小,因此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 << "B\n"; }
    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();
}
#包括
#包括
#包括
结构A{
虚空f()常量{/*不执行任何操作*/}
};
结构B:A{
国际组织;
B(inti):i{i}{}
void f()const override{std::cout sizeof(A)
,placement
new
-将
B
对象插入
向量
将覆盖以下对象的内存(或关闭
向量

我听说有一个东西叫做Copeland虚拟构造函数习惯用法,其中对象的类型更改类似于
操作符new(&a)B{}
(尽管在这种情况下,它可以从构造函数
操作符new(this)B{}
完成),但您必须是语言和/或实现专家,才能知道它是否/何时可以安全使用。

是的,这是可能的