在C++中使用指向数组中对象的指针更好吗
Is it better to use pointers to objects in an array in C++?
我正在涉猎C++,并试图用指针来理解它。创建对象数组时,创建对象类型的数组还是指针的数组更好?
例如:
Block grid[size];
或Block* grid[size];
从我读到的内容来看,我的印象是,当使用对象时,使用指针来节省内存几乎总是更好的?最终,我将创建一个由这些对象动态分配的2D数组,我想知道是否值得麻烦地尝试将它们作为指针存储在多维数组中。
注意:我被告知要研究2D向量,当涉及到动态创建和分配它们时,我和这些数组一样困难。
提前感谢!
我认为你在混合一些东西。经典的C数组是指向第一个元素的指针。你所想到的是在调用函数或类似函数时被(深度)复制的昂贵类。这些昂贵的函数参数(字符串、向量等)最好作为引用(或指针)传递。
"现代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
我会使用Block grid[size]
,因为你为什么要创建一个指针数组(我能想到的唯一原因是在编译时创建一个未知大小的数组)就内存而言,指针有点贵,因为它们的地址和内容都存储在堆栈和堆上。当你处理完它们时,你不能忘记释放它们,否则你会出现内存泄漏。
更好的选择是
std::array<Block, size> grid
,甚至是std::vector<Block> grid
。
std::vector
类似于std::array
,但它是动态的,没有固定的大小(例如,您可以在运行时更改它)。
假设这是在一个函数中,它不会"保存"内存。一种实现使用更多的堆栈内存。另一种使用较少的堆栈内存,但使用较多的堆。
这完全使用堆栈:
Block grid[size];
这使用堆栈来存储指向对象的指针。用于存储对象的内存是堆:
Block* grid[size];
然而,如果这些是全球声明,那么是的,这是:
Block grid[size];
可能会浪费内存,因为它是为应用程序的生存期分配的,并且无法释放/调整大小。
"更好"的答案取决于您如何使用数组。
如果Block很小(2-8字节),或者你需要使用网格的每个元素,你最好做
Block grid [size] ;
几乎在所有情况下。
假设sizeof(Block)>512,并且您没有使用网格和的所有元素
const int size = 10 ;
使用可能会更好
Block grid [size] ;
这可能是最有意义的。另一方面,假设
const in size = 2000000 ;
然后
Block *grid [size] ;
更有意义。
更好是一个品味问题:-)
在简单的用例中,对象数组可能更容易使用,因为您可以一次创建和销毁整个数组。它看起来更像C++惯用语言。
我能想到的例外是:
- 性能原因与创建对象的成本有关。在这里,您只为具有最大大小的指针创建一个数组,并在需要时创建对象
- 派生类的多态性。IMHO这是使用指针数组的最佳用例。因为如果你试图将派生对象复制到它的基类中,你会得到对象切片:基类副本将丢失只存在于派生类中的所有属性。因此,在这种情况下,您可以创建一个指向基类的指针数组(它甚至可以是抽象的),并用具体的实现填充它。您可以使用基类中的虚拟方法来操作数组中的任何对象
- 对象指针在c++中是如何工作的
- C++ 对象指针数组的复制构造函数
- 在对象指针上调用 Delete 是否会递归删除其动态分配的成员
- 什么更好?返回对象指针列表?或返回指向对象列表的指针?
- 正确初始化和销毁对象指针的C++数组?
- 如何深度复制链表对象指针
- 对象指针 c++ 的全局向量错误
- 如何将 c++ 类包装到 python 中,以便我可以使用 pybind11 访问其成员的公共方法(成员是一个对象指针)
- 静态对象指针
- 正在将对象指针数组初始化为NULL
- 如何使用条件表达式返回对象指针?
- std::flush可以用于将对象指针转换为其封闭数组指针吗
- 使用C对象指针构建PyObject*
- 如何在使用对象指针时访问成员函数
- 静态强制转换允许转换对象指针,但不允许转换整数
- C++ abort() 在函数内的抽象类对象指针调用上
- 指向函数的对象指针
- 访问指向对象指针向量的指针的第一个元素?
- 如何将对象/指针正确存储到 Qlist 中
- 对象指针打印结果以相反的顺序进行