我将使用这个简单结构的示例。我们假设 int 的大小是 4 个字节 - 这个结构的大小将是 8 个字节。
struct Point {
int x;
int y;
Point(char x, int y){
this->x = x;
this->y = y;
}
}
现在我们将在 nice 函数中创建 2 个变量。一个是普通变量,另一个是指针
void niceFunction(){
Point normalVariable(10, 5);
Point* pointer = new Point(10, 5);
}
niceFunction(); // we call our function
有什么区别?
第一个变量包含我们的整个结构。如果你使用sizeof(normalVariable),你会得到8个字节的结果。
第二个包含我们结构的地址。当您使用 sizeof(pointer) 时,您将获得 4 个字节 - 我们的结构位于动态内存中的某个位置,并且该指针仅包含指向我们结构的地址。
分配
分配是当我们为我们的结构请求内存时的行为。系统必须找到空闲内存并将其提供给我们。我们将用我们的东西填充这个内存(在我们的例子中,我们使用数字 10 和 5)。
在第一种情况下(普通变量),分配更快,因为系统使用 stack 来存放普通变量,因此知道你的东西存储在哪里以及空闲区域在哪里。这有一个问题 - 堆栈的大小有限,如果您分配给其中的许多结构,内存将被耗尽并且您的程序将被操作系统终止 - 称为 Stack Overflow
的错误
在第二种情况下(指针),系统必须在动态内存中找到空闲区域并在那里注入你的结构。这需要一些时间。你也有无限的内存(你只受你的内存大小和来自操作系统的限制)
解除定位
解除分配是将内存归还给系统的行为。返回的内存将在需要时再次使用
在第一种情况下,解除分配是自动。因为局部变量只存在于函数内部,所以一旦我们的函数结束,就没有任何理由从堆栈中释放它们。
在第二种情况(指针)中,我们的结构位于内存中的某个位置,因此系统不知道如何释放它。我们必须明确声明我们想要释放我们的结构
void niceFunction(){
Point normalVariable(10, 5);
Point* pointer = new Point(10, 5);
delete pointer; // this will dealocate our structure
}
如果我们忘记释放动态内存中的结构,它会一直留在这里直到程序结束。
参数传递/复制
想象一下有 2 个参数(点 a 和 b)的函数,它会返回它们的距离。我们将在第一个使用普通变量,在第二个使用指针。
float distanceNormal(Point a, Point b){
// body of function is irelevant for us
}
float distancePointer(Point* a, Point* b){
// body of function is irelevant for us
}
在第一种情况下,当我们将点传递给函数时,系统会复制它们。这意味着它将在函数内部的堆栈中分配新内存,并将整个结构复制到分配的内存中。如果您的结构很大或者它们的构造器正在执行昂贵的操作,这可能是无效的。这也意味着两个普通变量不能共享一个结构 - 它们可以具有相同的值,但它们不是相同的结构。
在第二种情况下,不会复制任何内容。相反,系统只会将地址传递给动态内存中的结构。这也会导致一种效果 - 当您使用指针修改结构时。它会改变原来的结构,因为它们都有相同的地址
Point* pointer = new Point(10, 5); // values of our point are (10, 5)
void changeX(Point* point){
point->x = 50;
}
changeX(pointer); // after this call, values of our point are (50, 5)
这被结构/对象的方法所利用。你在构造函数中看到了那个叫做 this 的奇怪变量吗?这也是指针* - 它是指向我们结构的指针。这允许我们直接操作结构的值。
结论
你可以看到他们都有优点和缺点。什么时候使用它们?
在以下情况下使用普通变量:
-
您不会在函数之外使用其内容。当你有一些只在 function 内部使用的东西时,没有任何理由在动态内存中分配它。 Stack 是更好的选择
-
你想要自动解除分配:在我们的例子中,普通变量将被自动解除分配。第二个(指针)必须手动释放。
-
您不想将其共享到函数中:当您使用指针时,您正在使用原始文件,并且更改将应用于原始文件。当您使用普通变量时,您正在使用副本,原始结构将不受影响
在以下情况下使用指针:
-
您想使用原始结构:如果您想在函数内部操作原始结构,请使用指针。
-
你想避免复制:如果你有结构并且你有被调用1000次的函数,使用普通变量是无效的。使用指针避免复制。
-
您想避免自动分配:有时,自动分配并不好。就像您为具有数百个字节的游戏分配的结构一样,在函数结束后,它将被释放。指针从未发生过这种情况,您必须使用 delete 手动释放它们。
黄金法则是:尽可能使用普通变量,必须使用指针