我现在正在阅读一本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();
}
我似乎无法在那里找到解释,为什么它这样做。
为什么我要以这种方式指向一个类,而不是使用标准的方式呢?
有什么区别?哪种情况会更好?
谢谢。
答案 0 :(得分:7)
首先,您不是指向类,而是指向类的实例,也称为对象。 (如果你问我的话,在C ++中指向类是不可能的,这是它的一个缺陷。)
区别在于分配对象的位置。当你在做的时候:
Calculator myCalc;
整个对象在stack上创建。堆栈是本地变量,嵌套调用等的存储,通常限制为1 MB或更低。另一方面,堆栈上的分配更快,因为不涉及内存管理器调用。
当你这样做时:
Calculator *myCalc;
除了在堆栈上分配Pointer之外,没有太多事情发生。指针通常为4或8字节(32位与64位架构),仅保存内存地址。您必须通过执行以下操作来分配对象并使指针指向它:
myCalc = new Calculator;
也可以组合成一行,如您的示例所示。这里,对象分配在heap上,大约与您的物理内存大小一样(不考虑交换空间和架构限制),因此您可以在那里存储更多数据。但它速度较慢,因为内存管理器需要启动并在堆上为您的对象找到一个备用位置,甚至需要从操作系统获取更多内存。现在指针myCalc
包含对象的内存地址,因此它可以与*
和->
运算符一起使用。
此外,您无法将指针或引用传递给其作用域外的堆栈上的对象,因为当作用域结束时(例如,在函数末尾),堆栈将被清除,因此对象变得不可用。
哦,差点忘了提。堆上的对象不会自动销毁,因此您必须手动删除它们,如*:
delete myCalc;
总而言之:对于不留下范围的小而短的生物对象,你可以使用基于堆栈的分配,而对于较大的长生命对象,堆通常是更好的去处。
*:理想情况下,不是那样的。使用智能指针,如std::unique_ptr
。
答案 1 :(得分:1)
两者都是标准的。一个不是优先于另一个。
第一个是您在狭窄范围内声明和使用的典型局部变量。
指针方法允许您动态分配内存并将其分配给指针类型;这就是“明星”符号的含义。这些可以从方法中传递出来或分配给成员变量,在退出方法后继续存在。
但是你必须要知道,当你完成指针指向的对象时,你也要负责清理那个内存。如果不这样做,那么很多人最终会耗尽长时间运行的“内存泄漏”。
答案 2 :(得分:1)
当您使用时,变量是类的实例或引用时使用点(。) - >如果你的变量是指向类实例的指针。
答案 3 :(得分:1)
它们都是C ++标准的一部分,但存在核心差异。在第一种方式中,您的对象存在于堆栈中(存储函数和局部变量的位置,并在不再使用它们后删除)。当您将变量类型声明为指针时,您只是在堆栈上存储指针,而对象本身就在堆上。
当您使用堆栈局部变量分配内存时,它会由C ++自动处理。当它在堆上时,您必须使用new
获取内存并将其与delete
一起释放。
在堆栈示例中,您的代码使用.
来调用方法,在指针上调用方法,C ++提供了一个快捷方式:->
,相当于*obj.method()
。
请记住,当您使用new
时,请始终use delete
。
答案 4 :(得分:0)
如果变量myCalc
具有非常长的生命周期,则可以使用一种方法。您可以在需要时使用new
创建它,并在完成delete
时将其删除。然后,您不必担心在不需要时携带它并且只占用空间。或者您可以在需要时随意重新初始化等等。
或者当你有一个非常大的类时,通常的做法是使用new
在堆上而不是堆栈上分配它。这是堆栈空间稀缺且堆积较大的时候的剩余,因此堆空间更便宜。
当然,最常见的用途是分配动态数组。 myCalc = new Calculator[x]
;创建x
个新计算器。如果您事先不知道x
的大小,则无法使用静态变量执行此操作;你要创建多少个对象。
答案 5 :(得分:0)
除了符号/语法的明显区别。将数据传递给函数时,指针通常很有用。
void myFunc(Calculator *c) {
...
}
通常优先于
void myFunc(Calculator c) {
...
}
因为第二个要求复制计算器。指针只包含指向的位置,因此它只引用内存中的另一个点而不是包含数据本身。另一个很好用的是字符串,想象一下读取文本文件并调用函数来处理文本,如果字符串不是指针,每个函数都会复制字符串。指针是4或8个字节,具体取决于机器的体系结构,因此在将其传递给函数时可以节省大量的时间和内存。
在某些情况下,使用副本可能更好。也许你只想返回一个像这样的修改版本
Calculator myFunc(Calculator c) {
...
}
关于指针的一个重要事项是“new”关键字。它不是创建指针的唯一方法,但它是c ++最简单的方法。你也应该能够使用一个名为malloc()的函数,但更多的是结构和c IMO,但我已经看到了两种方式。
说到C.指针也可能对阵列很有用。我认为你仍然只能在c ++的编译时声明一个数组的大小,但我可能会弄错。您可以使用以下我相信
Calculator *c;
....
Calculator d = c[index];
所以现在你有一个数组可以让它变得模糊不清IMO。
我认为这涵盖了我所知道的所有内容,并且在示例中我不认为您提供的两个片段之间存在任何差异。
答案 6 :(得分:0)
首先,您没有指向某个类,而是指向该类的实例(或对象)。在其他一些语言中,类实际上也是对象: - )
这个例子就是一个例子。很可能你不会在那里使用指针。
现在,什么是指针?指针只是一个指向真实事物的小东西。就像门铃上的名牌一样 - 它显示了你的名字,但实际上并不是你的名字。但是,因为不是你,你实际上可以在不同的位置有多个带有你名字的按钮。
这是使用指针的一个原因:如果你有一个对象,但你想在不同的地方保持指向该对象的指针。我的意思是,现实世界在各种各样的地方都有很多“指针”;不应该想象程序在数据中可能需要类似的东西。
指针也用于避免必须复制对象,这可能是一项昂贵的操作。将指针传递给函数要便宜得多。此外,它允许函数修改对象(请注意,从技术上讲,C ++“引用”也是指针,它只是稍微不那么明显,而且它们更受限制。)
此外,分配有“new”的对象将保持不变,直到用“delete”取消分配。因此,他们不依赖于范围界定 - 当他们周围的功能完成时,他们不会消失,只有当他们被告知迷路时才会消失。
另外,你会怎么做一个“水果袋”?您分配一个“包”对象。然后你分配一个“水果”对象,并在bag对象中设置一个指针指向水果对象,表明该包应该包含该水果。水果也可能会得到一个指向袋子对象的指针,因此在水果上工作的代码也可以进入袋子。你也可以分配另一个“水果”对象,并建立一个指针链:每个“水果”可以有一个指向“下一个”水果的“下一个”指针,所以你可以将任意数量的水果放入包里:包含指向第一个水果的指针,每个水果包含指向另一个水果的指针。所以你得到了一整套水果。 (这是一个简单的“容器”;有几个这样的类“包含”任意数量的对象。)
实际上并没有那么简单地描述使用指针的时间或原因;通常只会有你需要它们的情况。遇到这种情况时,更容易看到它们的用处。就像“为什么伞是有用的” - 一旦你踏入外面的倾盆大雨,伞的有用性将变得明显。