为什么需要一个指针来初始化堆上的对象,而不是堆栈上的对象?

时间:2013-04-18 11:03:50

标签: c++ class pointers stack heap

例如,在这两个代码中,一个不需要指针,另一个不需要。为什么是这样?如果myObject1不是指针,那究竟是什么?

class Object{
…
};


int main(){
    // Create instance on the stack
    Object myObject1;

    // Create instance on the heap
    Object *myObject2 = new Object;

    return 0;
}

感谢您的帮助。

7 个答案:

答案 0 :(得分:7)

您的两个声明都是具有自动存储持续时间的对象的定义。也就是说,它们都会在函数结束时被销毁。第一个是声明Object类型对象,第二个是Object*类型对象。

myObject2的初始化程序恰好是 new-expression 。新表达式动态分配对象并返回指向它的指针。正在使用指向动态分配的myObject2

的指针初始化Object

因此,您正在目睹创建对象的两种不同方式。一个是变量定义,一个是 new-expression

任何其他方式都没有意义。想象一下, new-expression 没有返回指向对象的指针,而是直接引用该对象。然后你可以这样写:

Object myObject2 = new Object();

但是,默认情况下,C ++使用值语义。这意味着动态分配的对象将被复制到myObject2,然后您已经丢失了它。您无法再获取该对象的地址。 new-expression 返回一个指针,以便您拥有动态分配对象的句柄。

你可能会说,“那就是我用Java写它的方式!”但那是因为Java以不同的方式工作。在Java中,myObject2是您设置为指向新Object对象的引用。它不会以任何方式复制对象。

C ++不要求在动态分配对象时必须使用指针。事实上,你可以做这样的事情(这是Java的等价物):

Object& myObject2 = *(new Object());

但这是一个非常糟糕的主意。它突然掩盖了这样一个事实,即对象是动态分配的,并且很容易犯错并忘记销毁对象(在Java中你不必关心它)。至少一个指针可能会提醒你这样做。但是,即使这样也会导致错误或不清楚的代码,这就是为什么建议您尽可能使用智能指针。

简而言之:这就是 new-expression 的行为方式。它动态分配一个对象,然后返回一个指向它的指针。

答案 1 :(得分:4)

它是类的实例(或对象)。 myObject2 指向到类的实例。

你也可以指向堆栈上的变量:

int main()
{
    Object myObject1;
    Object* pointerToObjectOnStack = &myObject1;
}

指针可指向任何地方;堆栈,堆或全局变量(既不在堆栈上也不在堆上)。

答案 2 :(得分:1)

  

例如,在这两个代码中,一个不需要指针,另一个不需要指针   确实。这是为什么?

因为你以一种(无保证的)尝试以这种方式编写它们以匹配你在上面写的评论。

  

如果myObject1不是指针,那究竟是什么?

Object。也就是说,Object类型的对象。也就是说,Object类的实例。

答案 3 :(得分:1)

myObject1是在堆栈中分配的Object

Here您将找到有关内存的更准确信息。

用两个词来说:

  1. 函数中的所有局部变量(包括对象实例)都在堆栈中分配。
  2. 所有动态分配(使用newmalloc())数据都在中分配。

答案 4 :(得分:1)

当你在堆栈上分配一个对象时,编译器会为你完成所有脏工作(分配/处理),因此你不需要一个指针来使用该对象。当您引用myObject1时,您正在引用Object本身,您只需访问其字段,方法等。

请注意,您始终可以使用&运算符获取指向堆栈变量的指针。

Object myObject1; // This is the live instance of Object
Object * pObject1 = &myObject1; // Here you can obtain a pointer
                                // to myObject1
myObject1.someField = 42;       // Accessing myObject1's data

另一方面,在堆上分配对象需要您管理这些对象(使用new / delete,malloc / free等手动分配/取消分配它们),所以首先得到指向内存的指针,其中对象是找到并使用它,您必须使用*运算符取消引用它。

Object * myObject1 = new Object(); // Here you construct the Object manually
                                   // and get the pointer to place, where Object
                                   // was allocated.
(*myObject1).someField = 42;       // Accessing myObject1's data, notice the 
                                   // dereference (*)
myObject1->someField = 42;         // The same, written more easily

答案 5 :(得分:0)

当函数与一些元数据一起被调用时,作为函数一部分的每个对象都被分配在堆栈上。当编译器编译函数时定义该元数据结构 - >这导致从函数中快速访问那些变量(相对位置不会改变)。当函数返回时,这个内存总是(自动)释放。

虽然堆在内存的不同部分设置,但可以或不能根据逻辑使用。

你有一个错误,通过将代码分成两行很容易显示:

class Object{
…
};

int main(){
    // Create instance of Object on the stack
    Object myObject1;

    // Create instance of a pointer to Object on the stack
    Object *myObject2;

    // Create instance of Object on the Heap and assigned its address to the pointer.
    myObject2 = new Object;

    return 0;
}

答案 6 :(得分:-3)

如果在堆栈上分配变量,编译器会隐式为您生成指针。

即。 Object myObject1; myObject1.foo(); 将编译为

Object* myObject1_implicit_ptr = new(alloca(sizeof(Object))) Object;
myObject1_implicit_ptr->foo();

alloca在堆栈上分配内存)


Object myObject1;中,myObject1是对象实例所在的内存位置名称,即其地址的别名(相对于堆栈帧指针)。
我们可以用operator&查询该地址并存储到指针(一个包含地址的变量)中:

Object myObject1; // myObject1 == stack-frame-pointer + 123
Object* myObject1_ptr = &myObject1;