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

Object *myObject = new Object;

而不是:

Object myObject;

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

myObject.testFunc();

我们必须写下:

myObject->testFunc();

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


当前回答

主要问题是为什么我应该使用指针而不是对象本身?我的回答是,你应该(几乎)永远不要使用指针而不是对象,因为C++有引用,它比指针更安全,并且保证了与指针相同的性能。

你在问题中提到的另一件事:

Object *myObject = new Object;

它是如何工作的?它创建Object类型的指针,分配内存以适应一个对象,并调用默认构造函数,听起来不错,对吧?但实际上,如果你动态分配内存(使用关键字new),你还必须手动释放内存,这意味着在代码中你应该:

delete myObject;

这调用析构函数并释放内存,看起来很简单,但在大型项目中,可能很难检测一个线程是否释放了内存,但为此,您可以尝试共享指针,这会稍微降低性能,但使用它们要容易得多。


现在一些介绍已经结束,回到问题上来。

在函数之间传输数据时,可以使用指针而不是对象来获得更好的性能。

看看,你有std::string(它也是对象),它包含了很多数据,例如大XML,现在你需要解析它,但为此你有一个函数void foo(…),它可以用不同的方式声明:

void foo(std::string xml);在这种情况下,您将把变量中的所有数据复制到函数堆栈中,这需要一些时间,因此性能会很低。void foo(std::string*xml);在这种情况下,您将以与传递size_t变量相同的速度将指针传递给对象,但这种声明容易出错,因为您可以传递NULL指针或无效指针。指针通常在C中使用,因为它没有引用。void foo(std::string&xml);这里传递引用,基本上和传递指针一样,但编译器做了一些事情,不能传递无效引用(实际上,可能会使用无效引用创建情况,但这会使编译器感到棘手)。void foo(const std::string*xml);这里与第二个相同,只是指针值不能更改。void foo(const std::string&xml);这里与第三个相同,但对象值不能更改。

我还想提的是,无论您选择了哪种分配方式(新的还是常规的),您都可以使用这5种方式传递数据。


另一件事要提的是,当您以常规方式创建对象时,您会在堆栈中分配内存,但当您使用新对象创建对象时会分配堆。分配堆栈要快得多,但对于真正大的数据数组来说,它有点小,所以如果你需要大对象,你应该使用堆,因为你可能会遇到堆栈溢出,但通常这个问题是使用STL容器解决的,记住std::string也是容器,有些人忘记了:)

其他回答

这个问题有很多很好的答案,包括前向声明、多态性等重要用例,但我觉得你的问题的“灵魂”部分没有得到回答,即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#)语义。

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

我有一个简单的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倍,这仍然很可观。

“需要是发明之母。”我想指出的最重要的区别是我自己编码经验的结果。有时需要将对象传递给函数。在这种情况下,如果您的对象是一个非常大的类,那么将其作为对象传递将复制其状态(您可能不希望..AND可以是big OVERHEAD),从而导致复制对象的开销。而指针是固定的4字节大小(假设为32位)。上面已经提到了其他原因。。。

使用指针的一个原因是与C函数接口。另一个原因是节省内存;例如:不要将包含大量数据且具有处理器密集型复制构造函数的对象传递给函数,只需将指针传递给该对象即可节省内存和速度,尤其是在循环中,但在这种情况下引用会更好,除非您使用的是C样式数组。

C++中对象指针的关键优势是允许同一超类的指针的多态数组和映射。例如,它允许将长尾鹦鹉、鸡、知更鸟、鸵鸟等放在鸟的阵列中。

此外,动态分配的对象更灵活,可以使用HEAP内存,而本地分配的对象将使用STACK内存,除非它是静态的。堆栈上有大型对象,尤其是使用递归时,无疑会导致堆栈溢出。