【问题标题】:Why is a pointer needed for initialising an object on the heap, but not on the stack?为什么在堆上初始化对象需要指针,而不是在堆栈上?
【发布时间】:2013-04-11 11:07:01
【问题描述】:

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

class Object{
…
};


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

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

    return 0;
}

感谢您的帮助。

【问题讨论】:

  • 指针不是必需的,它只是一种方便的方式来记住对象的位置。仅使用new Object; 也会在堆上创建一个对象,但是我们不知道它在哪里。

标签: c++ class pointers heap-memory stack-memory


【解决方案1】:

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

因为您以这种方式编写它们是为了(未保证)尝试匹配您在上面编写的 cmets。

如果 myObject1 不是指针,那么它到底是什么?

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

【讨论】:

  • myObject2也是一个对象,它是一个指向Object类型对象的指针。
  • @AlexeyFrunze 是的。我从不否认这一点。 myObject2 是 Object * 类型的对象,myObject1 是 Object 类型的对象。
【解决方案2】:

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

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

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

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

【讨论】:

    【解决方案3】:

    myObject1 是分配在堆栈中的Object

    Here你会找到更准确的内存信息。

    两个字:

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

    【讨论】:

      【解决方案4】:

      您的两个声明都是具有自动存储期限的对象的定义。也就是说,它们都将在函数结束时被销毁。第一个是声明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 的行为方式。它动态分配一个对象,然后返回一个指向它的指针。

      【讨论】:

        【解决方案5】:

        当您在堆栈上分配对象时,编译器会为您完成所有脏活(分配/解除分配),因此实际上您不需要指针来使用该对象。当您引用 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
        

        【讨论】:

          【解决方案6】:

          如果变量是在栈上分配的,编译器会为你隐式生成一个指针。

          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;
          

          【讨论】:

          • 如果你问我,这比澄清更令人困惑。虽然从技术上讲它是等效的,但我很难证明编译器“实际上”比其他任何事情都做得更多。
          • 虽然这可能反映了它是如何编译成机器代码的,但在内存中使用来自alloca 的位置new肯定不会帮助以任何方式了解 OP 问题的实际概念,并且可能弊大于利。
          • @MagnusHoff 好吧,在装配中它可能确实会类似地工作。为堆栈上的对象分配内存,并使用指向该内存的指针调用每个成员函数。但我完全同意这对任何人都没有帮助。
          • 问题是“为什么堆栈分配的对象不需要指针”。我写道它是必需的,但编译器隐藏了它。有什么好困惑的?
          • (不是我的反对票)。这不是现代编译器分配堆栈空间的方式。他们预先这样做,将所有变量结合起来,考虑到它们的生命周期。 IE。 char* _StackFrame = alloca(_Size); Object* _Ptr_myObject = _StackFrame + _Offset_myObject; new(_Ptr_myObject) Object;
          【解决方案7】:

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

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

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

          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;
          }
          

          【讨论】:

            猜你喜欢
            • 2017-05-27
            • 1970-01-01
            • 1970-01-01
            • 2012-02-05
            • 2020-10-27
            • 1970-01-01
            • 2013-11-08
            • 2017-09-24
            • 1970-01-01
            相关资源
            最近更新 更多