2

我现在正在阅读一本 C++ 书籍,我对指向类感到有些困惑。

本书前面的示例以这种方式使用类和方法:

Calculator myCalc;
myCalc.launch();

while( myCalc.run() ){
    myCalc.readInput();
    myCalc.writeOutput();
}

但是,现在它改为这样做:

Calculator* myCalc = new Calculator;
myCalc -> launch();

while( myCalc -> run() ){
    myCalc -> readInput();
    myCalc -> writeOutput();
}

而且我似乎无法在其中找到关于为什么这样做的解释。

为什么我要以这种方式指向一个类,而不是使用标准的方式呢?

有什么区别?什么情况下其中一种更可取?

谢谢你。

4

7 回答 7

9

首先,您指向的不是类,而是类的一个实例,也称为对象。(在 C++ 中不能指向类,如果你问我这是它的缺陷之一)。

不同之处在于分配对象的位置。当你在做:

Calculator myCalc;

整个对象是在堆栈上创建的。堆栈是局部变量、嵌套调用等的存储,通常限制为 1 MB 或更低。另一方面,堆栈上的分配更快,因为不涉及内存管理器调用。

当你这样做时:

Calculator *myCalc;

除了在堆栈上分配了一个指针外,没有发生太多事情。指针的大小通常为 4 或 8 字节(32 位与 64 位架构),并且只保存一个内存地址。您必须分配一个对象并通过执行以下操作使指针指向它:

myCalc = new Calculator;

也可以像您的示例中所示组合成一行。在这里,对象分配在堆上,它大约与您的物理内存一样大(不考虑交换空间和架构限制),因此您可以在那里存储更多数据。但它更慢,因为内存管理器需要启动并在堆上为您的对象找到一个备用位置,甚至需要从操作系统获取更多内存。现在指针myCalc包含了对象的内存地址,所以它可以与*->运算符一起使用。

此外,您不能在其范围之外传递指向堆栈上对象的指针或引用,因为堆栈将在范围结束时(例如,在函数结束时)被清理,因此对象变得不可用。

哦,差点忘了提。堆上的对象不会自动销毁,因此您必须像这样手动删除它们*:

delete myCalc;

总结一下:对于不会离开其范围的小而短的对象,您可以使用基于堆栈的分配,而对于较大的、长寿命的对象,堆通常是更好的选择。


*:嗯,理想情况下,不是那样的。使用智能指针,例如std::unique_ptr.

于 2012-07-21T14:35:03.273 回答
1

两者都是标准的。一个并不优于另一个。

第一个是您在狭窄范围内声明和使用的典型局部变量。

指针方法允许您动态分配内存并将其分配给指针类型;这就是“星”符号的含义。这些可以从方法中传递出来或分配给成员变量,在方法退出后继续存在。

但是你必须知道,当你处理完指针所指的对象时,你也有责任清理该内存。如果不这样做,很多人最终会因“内存泄漏”而耗尽长时间运行的应用程序。

于 2012-07-21T14:29:58.070 回答
1

当您的变量是类的实例或引用时,您使用点 (.),而如果您的变量是指向类实例的指针,则使用 ->。

于 2012-07-21T14:32:07.310 回答
1

它们都是 C++ 标准的一部分,但有一个核心区别。在第一种方式中,您的对象存在于堆栈中(这是存储函数和局部变量的地方,并在它们不再使用后被删除)。当您将变量类型声明为指针时,您只是将指针存储在堆栈上,而对象本身正在堆上。

而当您使用堆栈局部变量来分配内存时,它会由 C++ 自动处理。当它在堆上时,您必须使用 获取内存new并使用delete.

虽然在堆栈示例中,您的代码用于.调用方法、调用指针上的方法,但 C++ 提供了一个快捷方式:->,相当于*obj.method().

请记住,当您使用 时new,请始终使用delete

于 2012-07-21T14:35:04.313 回答
0

一种用途是变量myCalc的生命周期很长。您可以在需要时创建它 if withnew并在完成时删除它delete。这样您就不必担心在不需要它并且只会占用空间的时候随身携带它。或者您可以在需要时随意重新初始化它等。

或者当你有一个非常大的类时,通常的做法是new在堆而不是堆栈上分配它。这是堆栈空间稀缺且堆更大的日子的遗留物,因此堆空间更便宜。

或者,当然,最常见的用途是分配动态数组。myCalc = new Calculator[x]; 创建x新的计算器。如果您事先不知道有多大,则无法使用静态变量执行此操作x;你要创建多少个对象。

于 2012-07-21T14:38:16.660 回答
0

除了符号/语法的明显差异。将数据传递给函数时,指针通常很有用。

void myFunc(Calculator *c) {
    ...
}

通常优先于

void myFunc(Calculator c) {
    ...
}

因为第二个需要复制计算器。指针仅包含指向的位置,因此它仅指向内存中的另一个位置,而不是包含数据本身。另一个很好的用途是字符串,想象读取一个文本文件并调用函数来处理文本,如果它不是一个指针,每个函数都会复制一个字符串。指针是 4 字节或 8 字节,具体取决于机器架构,因此在将指针传递给函数时可以节省大量时间和内存。

在某些情况下,使用副本可能会更好。也许你只是想像这样返回一个改变的版本

Calculator myFunc(Calculator c) {
    ...
}

关于指针的重要事情之一是“new”关键字。它不是创建指针的唯一方法,但它是 c++ 最简单的方法。您还应该能够使用一个名为 malloc() 的函数,但这更适用于 structs 和 c IMO,但我已经看到了这两种方法。

说到 C。指针也可能适用于数组。我认为您仍然只能在 C++ 中的编译时声明数组的大小,但我可能弄错了。你可以使用以下我相信

Calculator *c;
....
Calculator d = c[index];

所以现在你有一个数组可以使它变得非常模糊 IMO。

我认为这几乎涵盖了我所知道的所有内容,并且在提供的示例中,我认为您提供的两个片段之间没有任何区别。

于 2012-07-21T14:45:01.653 回答
0

首先,您不是指向一个类,而是指向该类的一个实例(或对象)。在其他一些语言中,类实际上也是对象 :-)

例子就是这样,一个例子。很可能你不会在那里使用指针。

现在,什么是指针?指针只是一个指向真实事物的小东西。就像门铃上的名牌一样——它显示了你的名字,但实际上并不是你。但是,因为不是您,您实际上可以在不同位置有多个带有您的名字的按钮。

这是使用指针的原因之一:如果你有一个对象,但你想在不同的地方保存指向该对象的指针。我的意思是,现实世界在各个地方都有大量指向您的“指针”;不难想象程序可能在其数据中需要类似的东西。

指针还用于避免复制对象,这可能是一项昂贵的操作。传递指向函数的指针要便宜得多。另外,它允许函数修改对象(请注意,从技术上讲,C++“引用”也是指针,只是不太明显,而且受到更多限制)。

此外,使用“new”分配的对象将一直存在,直到使用“delete”解除分配。因此,它们不依赖于作用域——它们不会在它们周围的功能完成时消失,它们只会在它们被告知迷路时消失。

另外,您将如何制作“水果袋”?您分配一个“袋子”对象。然后分配一个“水果”对象,并在袋子对象内设置一个指针指向水果对象,表明袋子应该包含该水果。水果也可能得到一个指向袋子对象的指针,这样处理水果的代码也可以到达袋子。您还可以分配另一个“水果”对象,并建立一个指针链:每个“水果”可以有一个指向“下一个”水果的“下一个”指针,因此您可以将任意数量的水果放入袋子中:包中包含指向第一个水果的指针,每个水果包含指向另一个水果的指针。所以你得到了一整串的水果。(这是一个简单的“容器”;

想出何时或为什么使用指针的描述实际上并不是那么简单。通常只会在某些情况下您需要它们。当您遇到这种情况时,更容易看到它们的用处。就像“伞为什么有用”——一旦你踏入外面的倾盆大雨,伞的用处就显而易见了。

于 2012-07-21T14:46:23.597 回答