我来自Java背景,已经开始使用C++中的对象。但我想到的一件事是,人们经常使用指向对象的指针,而不是对象本身,例如这样的声明:

Object *myObject = new Object;

而不是:

Object myObject;

或者不用函数,让我们说testFunc(),如下所示:

myObject.testFunc();

我们必须写下:

myObject->testFunc();

但我不明白我们为什么要这样做。我认为这与效率和速度有关,因为我们可以直接访问内存地址。我说得对吗?


当前回答

在内存利用率很高的领域,指针很方便。例如,考虑一个极大极小算法,其中将使用递归例程生成数千个节点,然后使用它们来评估游戏中的下一个最佳移动,解除分配或重置(如智能指针)的能力显著减少了内存消耗。而非指针变量继续占用空间,直到其递归调用返回值。

其他回答

这个问题有很多很好的答案,包括前向声明、多态性等重要用例,但我觉得你的问题的“灵魂”部分没有得到回答,即Java和C++中不同的语法意味着什么。

让我们来比较一下这两种语言的情况:

Java语言:

Object object1 = new Object(); //A new object is allocated by Java
Object object2 = new Object(); //Another new object is allocated by Java

object1 = object2; 
//object1 now points to the object originally allocated for object2
//The object originally allocated for object1 is now "dead" - nothing points to it, so it
//will be reclaimed by the Garbage Collector.
//If either object1 or object2 is changed, the change will be reflected to the other

与此最接近的等效值为:

C++:

Object * object1 = new Object(); //A new object is allocated on the heap
Object * object2 = new Object(); //Another new object is allocated on the heap
delete object1;
//Since C++ does not have a garbage collector, if we don't do that, the next line would 
//cause a "memory leak", i.e. a piece of claimed memory that the app cannot use 
//and that we have no way to reclaim...

object1 = object2; //Same as Java, object1 points to object2.

让我们看看另一种C++方式:

Object object1; //A new object is allocated on the STACK
Object object2; //Another new object is allocated on the STACK
object1 = object2;//!!!! This is different! The CONTENTS of object2 are COPIED onto object1,
//using the "copy assignment operator", the definition of operator =.
//But, the two objects are still different. Change one, the other remains unchanged.
//Also, the objects get automatically destroyed once the function returns...

最好的方法是——或多或少——Java(隐式)处理指向对象的指针,而C++可以处理指向对象或对象本身的指针。这是有例外的——例如,如果您声明Java“原始”类型,它们是复制的实际值,而不是指针。所以

Java语言:

int object1; //An integer is allocated on the stack.
int object2; //Another integer is allocated on the stack.
object1 = object2; //The value of object2 is copied to object1.

也就是说,使用指针不一定是正确或错误的处理方式;然而,其他答案已经令人满意地涵盖了这一点。不过,总的想法是,在C++中,您可以对对象的生存期以及它们将生存的位置进行更多的控制。

重点是——Object*Object=newObject()构造实际上最接近典型的Java(或C#)语义。

Object *myObject = new Object;

这样做将创建对对象(堆上)的引用,必须显式删除该对象以避免内存泄漏。

Object myObject;

这样做将创建一个自动类型的对象(myObject)(在堆栈上),当对象(myObject)超出范围时,该对象将被自动删除。

tl;dr:不要“使用指针而不是对象本身”(通常)

你问为什么你更喜欢指针而不是对象本身。一般来说,你不应该。

现在,这条规则确实有多个例外,其他答案已经阐明了这些例外。问题是,这些天来,许多这些例外不再有效!让我们考虑一下公认答案中列出的例外情况:

您需要引用语义。

如果需要引用语义,请使用引用,而不是指针;参见@ST3的答案。事实上,有人会认为,在Java中,传递的通常是引用。

你需要多态性。

如果您知道要使用的一组类,通常可以使用std::variant<ClassA,ClassB,ClassC>(请参阅此处的描述),并使用访问者模式对它们进行操作。当然,C++的变体实现并不是最漂亮的;但我通常更喜欢它,而不是用指针弄脏。

您希望表示对象是可选的

绝对不要为此使用指针。您有std::可选的,与std::变体不同,它非常方便。请改用它。nullopt是空(或“null”)可选项。而且-这不是指针。

您希望分离编译单元以提高编译时间。

您也可以使用引用而不是指针来实现这一点。要在一段代码中使用Object&,只要说class Object;,即使用转发声明。

您需要与C库或C样式库交互。

是的,好吧,如果你使用已经使用指针的代码,那么-你必须自己使用指针,不能绕过这一点:-(而且C没有引用。


此外,有些人可能会告诉您使用指针来避免复制对象。由于返回值和命名的返回值优化(RVO和NRVO),这对于返回值来说并不是一个真正的问题。在其他情况下,参考文献可以很好地避免复制。

底线规则仍然与公认的答案相同:只有在有充分理由需要指针时才使用指针。


PS-如果你确实需要一个指针,你仍然应该避免直接使用new和delete。智能指针可能会更好地为您服务,它会自动释放(不像Java那样,但仍然如此)。

使用指针的另一个好理由是用于前向声明。在一个足够大的项目中,它们确实可以加快编译时间。

已经有很多很好的答案,但让我举一个例子:

我有一个简单的Item类:

 class Item
    {
    public: 
      std::string name;
      int weight;
      int price;
    };

我做了一个向量来容纳它们。

std::vector<Item>库存;

我创建了一百万个Item对象,并将它们推回到向量上。我按名称对向量进行排序,然后对特定项目名称进行简单的迭代二进制搜索。我测试了程序,完成执行需要8分钟。然后我改变我的库存向量如下:

std::vector<Item*>库存;

…并通过新建创建我的百万Item对象。我对代码所做的唯一更改是使用指向Items的指针,除了最后为清理内存而添加的循环。该程序运行时间不到40秒,或者比速度提高10倍还要快。编辑:代码位于http://pastebin.com/DK24SPeW通过编译器优化,在我刚刚测试过的机器上,它只增加了3.4倍,这仍然很可观。