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

Object *myObject = new Object;

而不是:

Object myObject;

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

myObject.testFunc();

我们必须写下:

myObject->testFunc();

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


当前回答

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

其他回答

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

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

但我不明白为什么我们要这样使用它?

如果您使用:

Object myObject;

在函数内部,此函数返回后,myObject将被销毁。因此,如果您不需要函数外的对象,这很有用。此对象将放在当前线程堆栈上。

如果在函数体内部写入:

 Object *myObject = new Object;

那么,一旦函数结束,myObject指向的Object类实例将不会被销毁,并且分配在堆上。

现在,如果您是Java程序员,那么第二个示例更接近于对象分配在Java下的工作方式。此行:Object*myObject=new Object;等效于java:Object myObject=new Object();。不同的是,在javamyObject下,它将被垃圾收集,而在c++下,它不会被释放,您必须在某处显式调用“delete myObject;”否则会导致内存泄漏。

自从c++11以来,您可以通过在shared_ptr/unique_ptr中存储值来使用安全的动态分配方式:新对象。

std::shared_ptr<std::string> safe_str = make_shared<std::string>("make_shared");

// since c++14
std::unique_ptr<std::string> safe_str = make_unique<std::string>("make_shared"); 

此外,对象通常存储在容器中,如map-s或vector-s,它们将自动管理对象的生命周期。

指针有很多用例。

多态行为。对于多态类型,指针(或引用)用于避免切片:

class Base { ... };
class Derived : public Base { ... };

void fun(Base b) { ... }
void gun(Base* b) { ... }
void hun(Base& b) { ... }

Derived d;
fun(d);    // oops, all Derived parts silently "sliced" off
gun(&d);   // OK, a Derived object IS-A Base object
hun(d);    // also OK, reference also doesn't slice

引用语义和避免复制。对于非多态类型,指针(或引用)将避免复制可能昂贵的对象

Base b;
fun(b);  // copies b, potentially expensive 
gun(&b); // takes a pointer to b, no copying
hun(b);  // regular syntax, behaves as a pointer

注意,C++11具有移动语义,可以避免将昂贵的对象复制到函数参数中并作为返回值。但是使用一个指针肯定会避免这些问题,并且允许在同一个对象上使用多个指针(而一个对象只能从一次移动)。

资源获取。在现代C++中,使用新运算符创建指向资源的指针是一种反模式。使用特殊的资源类(标准容器之一)或智能指针(std::unique_ptr<>或std::shared_ptr<>)。考虑:

{
    auto b = new Base;
    ...       // oops, if an exception is thrown, destructor not called!
    delete b;
}

vs.

{
    auto b = std::make_unique<Base>();
    ...       // OK, now exception safe
}

原始指针只能用作“视图”,而不能以任何方式涉及所有权,无论是通过直接创建还是通过返回值隐式创建。另请参阅C++常见问题解答中的问答。

更细粒度的生命周期控制每次复制共享指针(例如作为函数参数)时,它指向的资源都保持活动状态。常规对象(不是由new创建的,直接由您创建或在资源类中创建)在超出范围时被销毁。

在C++中,堆栈上分配的对象(使用Object对象;块内的语句)将只存在于声明的范围内。当代码块完成执行时,声明的对象将被销毁。而如果使用Object*obj=new Object()在堆上分配内存,则它们将继续驻留在堆中,直到调用delete obj。

如果我想在堆上创建一个对象,而不仅仅是在声明/分配该对象的代码块中使用该对象。

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