在 C++ 中使用指向数组中对象的指针会更好吗?

Posted

技术标签:

【中文标题】在 C++ 中使用指向数组中对象的指针会更好吗?【英文标题】:Is it better to use pointers to objects in an array in C++? 【发布时间】:2016-03-26 16:28:27 【问题描述】:

我正在涉足 C++ 并试图将我的头脑围绕在指针的事情上。创建对象数组时,创建对象类型数组还是指针数组好?

例如:

Block grid[size];Block* grid[size];

根据我一直在阅读的内容,我的印象是,在使用对象时,使用指针来节省内存几乎总是更好?最终,我将创建一个动态分配的这些对象的二维数组,我想知道是否值得尝试将它们作为指针存储在多维数组中。

注意:有人告诉我要研究 2D 向量,当涉及到动态创建和分配它们时,我和这些数组一样苦苦挣扎。

提前致谢!

【问题讨论】:

为什么不使用像std::vector&lt;Block&gt; grid(size);这样的标准容器? 就像我在注释中所说的那样,我在创建 2D 标准向量方面同样苦苦挣扎。然后同样的问题仍然适用,我是制作 Block 还是 Block* 类型的向量? 使用指针只会在你重复使用同一个对象时节省内存。例如,如果同一个 Block 在您的数组中出现多次,否则它会使用额外的 sizeof(Block*)*size 字节。 @Matt,谢谢!使用 std::vector<:vector>> 比我的方法更可取吗? @nickcorin 很好,这完全取决于您使用它的目的。它是否需要可调整大小(你说“动态分配”,不一定是同一件事)? 【参考方案1】:

我认为你在混合一些东西。经典的 C 数组 指向第一个元素的指针。您想到的是在调用函数或类似函数时被(深度)复制时代价高昂的类。这些昂贵的函数参数(字符串、向量等)最好作为引用(或指针)传递。

'Modern C++' 为您提供了足够的实用程序,如果您不编写自己的容器,则不应自行使用任何分配。您通常使用指针来指向在上下文中不属于您的事物。我还建议不要再使用经典的 C 数组。使用

array<Block, size> grid; // compile-time constant size

而不是

Block grid[size]; // compile-time constant size

array&lt;&gt; 将尝试进入堆栈。如果size不知道编译时,你必须在堆上分配内存。 STL 改为调用这些堆数组vector

vector<Block> grid(size); // runtime variable size
if (grid.size() > 0) 
   cout << grid[0]; // use grid like an array

堆数组的旧 C 风格方法是手动分配内存并在不再需要它时释放它。这很容易出错,除非您知道自己在做什么,否则应避免这样做。

如前所述,与普通旧数组(即指针)的区别在于调用函数时。在您必须传递指针和可能的数组大小之前

void fancy_old_function(Block* grid, size_t size) // BAD practise

    // do things


// ...

    Block grid[size];
    fancy_old_function(grid, size); // error prone
    ...
    // maybe alot later
    fancy_old_function(grid, 13); // ohoh! 13 < size?

在 C++ 和大对象中,您应该将它们作为引用传递(或使其成为指针),否则您的向量会被深度复制。

void fancy_new_function(vector<Block>& grid) // GOOD

   // do fancy stuff an potentially change grid

现在的缺点是,我们对动态和静态数组有不同的签名:

template <size_t N>
void fancy_new_function(array<Block, N>& grid) // GOOD

   // do fancy stuff an potentially change grid

对经典 C 数组的一个巨大改进是,我们始终隐式地知道数组的大小。

您可以像这样拨打您的fancy_new_function

 
   array<Block,size> grid1;
   fancy_new_function(grid1); // size is implicitly deduced
                              // or use grid1.size();

   vector<Block> grid2(size);
   fancy_new_function(grid2); // developer can use grid2.size()

HTH

【讨论】:

谢谢谢谢谢谢!这真的很好地解释了事情。我想我有点想太多了。【参考方案2】:

我会使用Block grid[size],因为你为什么要创建一个指针数组(我能想到的唯一原因是在编译时创建一个大小未知的数组)在内存方面,指针有点贵,因为它们的地址和它们的内容被存储(分别在堆栈和堆上)。当你完成它们时,你不能忘记释放它们,否则你会得到内存泄漏。


更好的选择是std::array&lt;Block, size&gt; grid,甚至是std::vector&lt;Block&gt; grid

std::vector 类似于std::array,但它是动态的,没有固定大小(例如,您可以将其更改为运行时)。

【讨论】:

我不知道为什么我没有将它添加到问题中,但是动态分配的原因是我在编译时不会知道大小。数组是类中的一个属性,将在运行时创建。 @nickcorin 好的,所以你可以使用std::vector【参考方案3】:

假设这是在一个函数中,它不会“节省”内存。 一种实现使用更多的堆栈内存。 另一个使用较少的堆栈内存,但使用更多的堆。

这完全使用了堆栈:

Block grid[size];

这使用堆栈来存储指向对象的指针。 用于存储对象的内存是堆:

Block* grid[size];

但是,如果这些是全局声明,那么是的,这是:

Block grid[size];

可能会浪费内存,因为它是在您的应用程序的生命周期内分配的,并且无法解除分配/调整大小。

【讨论】:

【参考方案4】:

“更好”的答案取决于您如何使用数组。

如果 Block 很小(2-8 字节)或者你需要使用 grid 的每个元素,你最好这样做

Block grid [size] ;

几乎在所有情况下。

假设 sizeof (Block) > 512 并且你没有使用 grid

的所有元素
  const int size = 10 ;

你可能会更好

 Block grid [size] ; 

可能是最有意义的。另一方面,假设

const in size = 2000000 ;

然后

Block *grid [size] ;

更有意义。

【讨论】:

【参考方案5】:

更好是一个品味问题 :-)

对象数组可能更容易用于简单的用例,因为您可以一次创建和销毁整个数组。它看起来更像 C++ 惯用语。

我能想到的例外是:

创建对象代价高昂的性能原因。在这里,您只为具有最大大小的指针创建一个数组,并在需要时创建对象 派生类的多态性。恕我直言,这是使用指针数组的最佳用例。因为如果您尝试将派生对象复制到其基类中,您将获得对象切片:基类副本将丢失仅存在于派生类中的所有属性。所以在这种情况下,你创建一个指向基类指针的数组(它甚至可以是抽象的)并用具体的实现来填充它。您可以使用基类中的虚拟方法来操作数组中的任何对象。

【讨论】:

以上是关于在 C++ 中使用指向数组中对象的指针会更好吗?的主要内容,如果未能解决你的问题,请参考以下文章

cvrelease 会删除分配的内存吗?

如何通过 C++ 中指向该数组的指针访问另一个类中的数组?

在c++编程中,怎么定义对象数组的指针?

我可以直接从 C++ 中向量元素的指针获取索引吗?

当函数返回时,指向超出范围的对象的 C++ 指针 - 为啥会这样?

指向基点的指针可以指向派生对象的数组吗?