使用new关键字创建一个结构体数组

6
我这里有一些困难情况,希望有人能帮助我。
我有以下的东西,
struct CandyBar{ 
  std::string name; 
  double weight; 
  int calories; 
} ;

现在,创建一个包含许多CandyBars的结构体数组非常简单,我已经像这样完成了;
int main(){
   CandyBar bars[] = {{"Mango tart", 2.3, 100}, {"Yo Yo", 3.2, 130}, {"Orange Ball", 1.7, 98}};
  return 0;
}

但是,现在我想使用新的方式创建相同的结构。当我想到这个时很简单,但是它却崩溃了并且无法工作。
CandyBar *bars = new CandyBar;
  (*bars).name = "Mango tart";
   bars->weight =  2.3;
   bars->calories =  100;
   bars += 1;

  (*bars).name = "Yo Yo";
  bars->weight =  3.2;
  bars->calories =  130;

  bars += 1;
  (*bars).name =  "Orange Ball";
  bars->weight =  1.7;
  bars->calories =  98; 

但是,这并不起作用。我认为第一个指针是指向第一个结构体的内存位置,然后我创建结构体,然后使用bar += 1增加地址,并继续创建指针,但我可能错过了一些非常重要的东西。
我真的很感激你的帮助。

2
你只分配了一个CandyBar的空间。 - jrok
我在C++方面的经验不是很丰富,但是vectorsharedptr听起来像是可行的选择。 - Lews Therin
你可能需要一个 std::vector<CandyBar> - fredoverflow
1
以上已有答案。但我可以问一下,你为什么会在 (*bars).bars-> 之间交替使用?看起来很奇怪。另外,当你修改了保存其地址的指针后,如何删除数组? - Mr Lister
这是来自C++ Primer Plus的编程练习。 - Sam
显示剩余2条评论
4个回答

16

在你的第二个示例中,你只分配了一个CandyBar结构,但你却将其当作数组来处理。这样做不会有好结果,并且可能导致缓冲区溢出。

如果要分配一个CandyBars数组,你需要告诉new你想要一个数组,方法如下:

CandyBar* bars = new CandyBar[3];

确保在删除你的 CandyBars 数组时,使用以下方式进行数组删除:

delete[] bars;

所有这些都说了,在C++的生产代码中,你通常会使用std::vector或std::array来处理这种情况,因为你不必像在这种特殊情况下那样处理那么多资源管理。

如果你必须在堆上动态分配一个数组并仍然使用RAII,我还会看一下boost::shared_array和boost::scoped_array。


谢谢。你也提到了delete[]。我以为它很难,现在我知道它有多简单了 ;) - user1834305

6

只为一个CandyBar分配空间,所以你正在写入未分配的堆内存。这是一个大问题,你真的很幸运它正在失败,因为这有可能会产生非常难以调试的问题

你可以像这样为更多的CandyBar分配空间:

CandyBar *bars = new CandyBar[3]; // 3 CandyBars.

请不要忘记使用类似于以下的数组删除器:
delete[] bars;

但是,仍然有可能超出所分配的CandyBar数量。在C++中,更为常用的方法是使用std::vector

在这个例子中,我为CandyBar提供缩写的构造函数,并将它们放入一个std::vector中。

#include <string>
#include <vector>

struct CandyBar{ 
  std::string name;
  double weight;
  int calories;
  CandyBar(std::string name, double weight, int calories)
  {
      this->name = name;
      this->weight = weight;
      this->calories = calories;
  }
};

int main()
{
    std::vector<CandyBar> bars;
    bars.push_back(CandyBar("Mango tart", 2.3, 100));
    bars.push_back(CandyBar("Yo Yo", 3.2, 130));
    bars.push_back(CandyBar("Orange Ball", 1.7, 98));

    return 0;
}

没有新的,没有删除,没有泄漏,没有破坏。
我还要补充一点,如果你使用的编译器支持C++11并且不想使用std::vector提供的动态分配内存,你可以考虑使用std::array。具体内容请参见这里

哇,太棒了。我不知道向量的存在。看起来使用向量会更容易。 - user1834305

1

Foo * a = new FooFoo 类型分配了恰好一个实例的空间。如果您想要分配一个 Foo 数组,您需要使用 Foo * a = new Foo[ARRAY_LEN]


基本上,您想要做的是动态分配一些内存来保存对象数组,以您的情况为例是 CandyBar 对象。问题在于,您正在使用 new 运算符,该运算符仅为一个对象分配内存。然后,您尝试将返回的内存地址用作指向数组的指针。您不可避免地会尝试访问尚未分配的内存,这将导致分段错误(最好的情况)。

您真正寻找的运算符是 new[] 运算符(作为 new CandyBar[ARRAY_LEN] 调用),它将为指定数量的对象分配足够的内存。然后,您可以采用返回的内存地址,并将其视为所提供大小的数组,而无需担心超出内存分配。

一般来说,如果你正在处理一个可能在程序生命周期内增长或缩小的集合,那么分配固定大小的数组不是正确的方法。看看C++ STL中的std::vector模板类-std::vector可以根据你想要存储多少个CandyBar而动态地增长或缩小,并且可以自由访问,就像它是一个数组(例如,作为foo[42])。

你提到了new[],我也可以使用new[]代替new CandyBar[20]吗? - user1834305
@NSProxy 即使我们称之为 new[] 运算符,我们也是使用 new CandyBar[20] 的形式来调用该运算符;这只是语法的工作方式。 - atomicinf

0
你应该使用以下代码为 CandyBar 分配一个数组:
CandyBar *bars = new CandyBar[20];

然后你可以使用 bars[0], bars[1] ... bars[19] 等来访问单个糖果棒。

请注意,你的代码 bars + 1 将一个 sizeof CandyBar 添加到指针中,但这会指向未分配的区域(因为你只分配了一个 CandyBar)。因此你的代码会崩溃。


不,你不应该这样做。实际上,几乎没有任何情况需要使用数组“new”。 - James Kanze
当然,有更好的STL解决方案,其他答案也提供了很好的建议。我只是展示如何使用new来创建数组。 - Ben Ruijl
但是在任何情况下,您都不会使用new来创建数组。永远不会。 - James Kanze

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