首页 > 解决方案 > 当子元素位于父类向量中时,访问父类中的子成员

问题描述

我在父类向量中有多个子类,每个子类都有自己的类型。父母有一个虚getType函数,每个孩子都用自己的虚函数覆盖它;甚至不确定我是否需要这个 TBH,但我从父类 C++ 中的这个 Access 子成员中获得

当我遍历向量时(此处未显示循环),类型只是父级的类型,因为它是父级的向量,但我需要使用其自己的构造函数创建的每个子级的类型。

#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Parent 
{
  private:
  string type = "parent";
  public:
    virtual string getType()
    {
      return type;
    }

};
class Child1 : public Parent
{
  string type = "type1";
  public:
    string getType()
    {
      return type;
    }
};
class Child2 : public Parent
{
  string type = "type2";
  public:
    string getType()
    {
      return type;
    }
};
//main.cpp
int main()
{
   vector<Parent> children;
   Child1 t1;
   Child2  t2;
   children.push_back(t1);
   children.push_back(t2);
   //THIS WORKS
   cout << t1.getType(); // returns type1
   // I NEED THIS TO WORK
   cout << children[0].getType(); // returns parent. 
   cout << children[1].getType(); // I need type1 && type2

}

我怎样才能做到这一点?我无法知道每个孩子是什么类型的孩子,还是有其他方法可以做到这一点?

标签: c++

解决方案


您刚刚尝试了切片

AChildParent加了一些东西。

当你尝试将 aChild放入 的向量中时Parent,只有其中Parent每个Children 的一部分放入向量中(因为向量中不包含Children 而是包含Parents);因此得名slicing

为了获得您正在寻找的动态多态性,您需要一个指向 ; 的指针向量Parent这样,每个指向的元素都可以是 aParent或 aChild并相应地表现。

这通常通过动态分配向量中的每个元素来完成,但这不是强制性的。例如,您可以将所有Parents 存储在一个向量中,将所有Children 1 存储在另一个中,依此类推,最后使用指针向量以任意顺序指定其中一些。

如果您决定单独分配每个Parent/ Child,您应该考虑智能指针,std::unique_ptr<T>而不是原始指针和new/ delete

您将在下面发现您的示例稍作修改以获得动态多态性。std::unique_ptr<T>由于/ ,它依赖于每个元素的动态分配std::make_unique()

请注意,由于动态多态性,您需要一个virtual析构函数(即使它没有什么特别的作用)。由于这种类型的层次结构旨在用于动态多态性,因此鼓励通过禁止使用复制/移动操作来防止切片(您刚刚经历过)。因此,您必须提供一个或多个满足您需求的构造函数(但这很常见)。

我的最后一条建议是«避免动态多态性;更喜欢template» 但这是另一个话题 ;^)

/**
  g++ -std=c++17 -o prog_cpp prog_cpp.cpp \
      -pedantic -Wall -Wextra -Wconversion -Wno-sign-conversion \
      -g -O0 -UNDEBUG -fsanitize=address,undefined
**/

#include <iostream>
#include <string>
#include <vector>
#include <memory> // std::unique_ptr<T>, std::make_unique()

class Parent 
{
public:
  virtual ~Parent() =default; // ensure correct destruction

  // forbid copy and move in order to prevent slicing
  Parent(const Parent &) =delete;
  Parent &operator=(const Parent &) =delete;
  Parent(Parent &&) =delete;
  Parent &operator=(Parent &&) =delete;

  Parent() =default; // provide a suitable constructor

  virtual
  const std::string &
  getType() const
  {
    return type;
  }
private:
  // inline static // is probably better
  const std::string type{"parent"};
};

class Child1: public Parent
{
public:
  const std::string &
  getType() const override
  {
    return type;
  }

private:
  // inline static // is probably better
  const std::string type{"type1"};
};

class Child2 : public Parent
{
public:
  const std::string &
  getType() const override
  {
    return type;
  }

private:
  // inline static // is probably better
  const std::string type{"type2"};
};

int
main()
{
  const auto p=Parent{};
  std::cout << "p: " << p.getType() << '\n';
  const auto c1=Child1{};
  std::cout << "c1: " << c1.getType() << '\n';
  const auto c2=Child2{};
  std::cout << "c2: " << c2.getType() << '\n';
  auto people=std::vector<std::unique_ptr<Parent>>{};
  for(auto i=0; i<2; ++i)
  {
    people.emplace_back(std::make_unique<Parent>());
    people.emplace_back(std::make_unique<Child1>());
    people.emplace_back(std::make_unique<Child2>());
  }
  for(const auto &e: people)
  {
    std::cout << e->getType() << '\n';
  }
  return 0;
}

推荐阅读