如何在C++中创建抽象类数组

8
我正在尝试创建一个程序,用于接受食品订单并打印出来。我有一个基类Food,其中包含一个纯虚函数。类Food有两个子类PizzaDessert。我想在main中创建一个Food数组,以便当客户点餐PizzaDessert时,它将被存储在Food数组中。但每次尝试时,我都会遇到错误。如果我想使用循环遍历客户点的每个物品,应该如何将这两个项目组合在一起呢? 以下是我的代码:
int main()
{
  Dessert d("brownie");
  Pizza p("BBQ delux");
  Food array[2] = {d,p};
}

这是我的错误信息。(注意:get_set_price()print_food()是我在基类中定义并在两个子类中实现的纯虚函数。)
main.cpp:37:14: error: invalid abstract typeFoodforarrayFood array[2] = {d,p};

In file included from main.cpp:4:0:
Food.h:5:7: note:   because the following virtual functions are pure within ‘Food’:
 class Food
       ^

Food.h:20:15: note:     virtual void Food::get_set_price()
  virtual void get_set_price()=0;
               ^

Food.h:27:15: note:     virtual void Food::print_food()
  virtual void print_food()=0; 
               ^

main.cpp:37:22: error: cannot allocate an object of abstract typeFoodFood array[2] = {f,o};
                  ^

4
使用指针或引用。 - Some programmer dude
@Someprogrammerdude "C++标准§8.3.2/4:不得有对引用的引用,不得有引用的数组,也不得有引用的指针。" -> 所以,引用不是一个选择! - undefined
虽然我的评论本意是更通用的,但仍然可以使用std::reference_wrapper来解决这个问题。 :) - undefined
3个回答

24

您无法创建抽象类的实例,但可以将具体派生实例的指针或引用分别赋给基类的指针或引用。

int main()
{
  Dessert d("brownie");
  Pizza p("BBQ delux");
  Food* array[2] = {&d,&p};
}

然后与数组一起使用

array[0]->print_food();

11

你需要引用语义来实现这个,因为 Food arr[2]; 试图使用默认值(抽象的,因此不能构造)初始化数组。

我认为在这种情况下,std::array<std::unique_ptr<Food>, 2> arr; 应该是最自然的选择。

std::array<std::unique_ptr<Food>> arr = {
    std::make_unique<Dessert>("brownie"),
    std::make_unique<Pizza>("BBQ delux")
};

不过,如果您只想循环这两个值,使用 initializer_list 可能是最简单的。

for (auto f : std::initializer_list<Food*>{&d,&p})
    f->commonMemberFunction();

不幸的是,它不能仅从{}中推断出正确的类型,但是可以创建一个帮助程序。


1
@TartanLlama 似乎不会 - Bartek Banachewicz
谢谢您的回复,@Bartek。您能告诉我unique_ptr是什么吗?它是普通指针还是具有其他属性? - Safwan Ull Karim
@ SafwanUllKarim 这是一个指针,保证该值的唯一、独占式拥有权。详见参考文献 - Bartek Banachewicz
1
唯一的问题是你必须确保在结束时为所有使用 new 的内存进行解除分配。 - Andrei-Niculae Petre
@AndreiPetre,这不是问题,因为它们都存储在智能指针中。 - Bartek Banachewicz
@BartekBanachewicz 谢谢,我不知道智能指针!好的,在这种情况下没问题,当智能指针(因此是数组)超出范围时,它将自动删除内存。 :) - Andrei-Niculae Petre

2

从C++11开始,你也可以使用std::reference_wrapper。它与@Mykola的答案非常相似,但使用引用:

#include <functional>  // for std::reference_wrapper

int main()
{
  Dessert d("brownie");
  Pizza p("BBQ delux");
  std::reference_wrapper<Food> array = {d, p};

  // just to see they're references, they have same memory addresses as
  // the initial variables.
  for (const Food &f : array) {
    std::cout << &f << " ";
  }
  std::cout << "\n" << &d << " " << &p << "\n";
}

很遗憾,这个限制也适用于带有指针的版本。你需要定义局部变量,不能在这里使用匿名对象。

以下代码无法正常工作:

  std::reference_wrapper<Food> array = {Dessert("brownie"), Pizza("BBQ delux")};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接