在 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<Block> 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<>
将尝试进入堆栈。如果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<Block, size> grid
,甚至是std::vector<Block> 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++ 中使用指向数组中对象的指针会更好吗?的主要内容,如果未能解决你的问题,请参考以下文章