什么时候对象“超出范围"?

2022-01-04 00:00:00 destructor scope c++

在 C++ 中,何时将对象定义为超出范围"?

In C++, when is an object defined as "out of scope"?

更具体地说,如果我有一个单链表,什么将单个列表节点对象定义为超出范围"?或者,如果一个对象存在并且正被一个变量 ptr 引用,那么在引用被删除或指向一个不同的对象时,该对象被定义为超出范围"是否正确?

More specifically, if I had a singly linked list, what would define a single list node object as "out of scope"? Or if an object exists and is being referenced by a variable ptr, is it correct to say that the object is defined as "out of scope" the moment the reference is deleted or points to a different object?

更新:假设一个对象是一个具有已实现析构函数的类.是否会在对象退出作用域的那一刻调用析构函数?

UPDATE: Assuming an object is a class that has an implemented destructor. Will the destructor be called the moment the object exits the scope?

if (myCondition) {
    Node* list_1 = new Node (3);
    Node* list_2 = new Node (4);
    Node* list_3 = new Node (5);

    list_1->next = list_2;
    list_2->next = list_3;
    list_3->next = null;
}

换句话说,list_1 所指向的 Node 是否会在此行之后调用其析构函数:

In other words, would the Node being pointed to by list_1 call its destructor after this line:

Node* list_1 = new Node (3);

?

推荐答案

首先,请记住,C++ 中的对象可以在堆栈上或在堆上创建.

First, remember that objects in C++ can be created either on the stack or on on the heap.

堆栈帧(或范围)由语句定义.它可以像函数一样大,也可以像流控制块一样小(while/if/for 等).包含任意代码块的任意 {} 对也构成了堆栈帧.一旦程序退出该框架,任何在框架内定义的局部变量都将超出范围.当堆栈变量超出范围时,将调用其析构函数.

A stack frame (or scope) is defined by a statement. That can be as big as a function or as small as a flow control block (while/if/for etc.). An arbitrary {} pair enclosing an arbitrary block of code also constitutes a stack frame. Any local variable defined within a frame will go out of scope once the program exits that frame. When a stack variable goes out of scope, its destructor is called.

所以这是一个堆栈帧(函数的执行)和在其中声明的局部变量的经典示例,一旦堆栈帧退出 - 一旦函数完成,该局部变量将超出范围:

So here is a classic example of a stack frame (an execution of a function) and a local variable declared within it, which will go out of scope once the stack frame exits - once the function finishes:

void bigSideEffectGuy () {
    BigHeavyObject b (200);
    b.doSomeBigHeavyStuff();
}
bigSideEffectGuy();
// a BigHeavyObject called b was created during the call, 
// and it went out of scope after the call finished.
// The destructor ~BigHeavyObject() was called when that happened.

下面是一个例子,我们看到一个栈帧只是一个 if 语句的主体:

Here is an example where we see a stack frame being just the body of an if statement:

if (myCondition) {
    Circle c (20);
    c.draw();
}
// c is now out of scope
// The destructor ~Circle() has been called

堆栈创建的对象在退出框架后保留在范围内"的唯一方法是它是否是函数的返回值.但这并不是真正的保留在范围内",因为对象正在被复制.所以原件超出范围,但制作了副本.示例:

The only way for a stack-created object to "remain in scope" after the frame is exited is if it is the return value of a function. But that is not really "remaining in scope" because the object is being copied. So the original goes out of scope, but a copy is made. Example:

Circle myFunc () {
    Circle c (20);
    return c;
}
// The original c went out of scope. 
// But, the object was copied back to another 
// scope (the previous stack frame) as a return value.
// No destructor was called.

现在,也可以在堆上声明一个对象.为便于讨论,将堆视为无定形的内存块.与在您进入和退出堆栈帧时自动分配和取消分配必要内存的堆栈不同,您必须手动保留和释放堆内存.

Now, an object can also be declared on the heap. For the sake of this discussion, think of the heap as an amorphous blob of memory. Unlike the stack, which automatically allocates and de-allocates the necessary memory as you enter and exit stack frames, you must manually reserve and free heap memory.

在堆上声明的对象以某种方式在堆栈帧之间存活".可以说在堆上声明的对象永远不会超出范围,但这实际上是因为该对象从未真正与任何范围相关联.这样的对象必须通过 new 关键字创建,并且必须由指针引用.

An object declared on the heap does, after a fashion, "survive" between stack frames. One could say that an object declared on the heap never goes out of scope, but that's really because the object is never really associated with any scope. Such an object must be created via the new keyword, and must be referred to by a pointer.

完成后释放堆对象是您的责任.您可以使用 delete 关键字释放堆对象.在释放对象之前,不会调用堆对象上的析构函数.

It is your responsibility to free the heap object once you are done with it. You free heap objects with the delete keyword. The destructor on a heap object is not called until you free the object.

指向堆对象的指针本身通常是与作用域相关的局部变量.使用完堆对象后,您允许引用它的指针超出范围.如果您没有明确释放指针所指向的对象,那么在进程退出之前永远不会释放堆内存块(这称为内存泄漏).

The pointers that refer to heap objects are themselves usually local variables associated with scopes. Once you are done using the heap object, you allow the pointer(s) referring to it to go out of scope. If you haven't explicitly freed the object the pointer is pointing to, then the block of heap memory will never be freed until the process exits (this is called a memory leak).

这样想:在堆栈上创建的对象就像一个气球,贴在房间里的椅子上.当您离开房间时,气球会自动弹出.在堆上创建的对象就像丝带上的气球,系在房间的椅子上.功能区是指针.当你离开房间时,丝带会自动消失,但气球只是漂浮到天花板上并占据空间.正确的做法是用别针戳破气球,然后离开房间,然后缎带就会消失.但是,绳子上的气球的好处是你也可以解开丝带,拿在手里,然后走出房间,带着气球.

Think of it all this way: an object created on the stack is like a balloon taped to a chair in a room. When you exit the room, the balloon automatically pops. An object created on the heap is like a balloon on a ribbon, tied to a chair in a room. The ribbon is the pointer. When you exit the room, the ribbon automatically vanishes, but the balloon just floats to the ceiling and takes up space. The proper procedure is to pop the balloon with a pin, and then exit the room, whereupon the ribbon will disappear. But, the good thing about the balloon on the string is you can also untie the ribbon, hold it in your hand, and exit the room and take the balloon with you.

因此转到您的链表示例:通常,此类列表的节点在堆上声明,每个节点都持有指向下一个节点的指针.所有这些都在堆上,永远不会超出范围.唯一可能超出范围的是指向列表根的指针 - 您首先用来引用列表的指针.这可能超出范围.

So to go to your linked list example: typically, nodes of such a list are declared on the heap, with each node holding a pointer to the next node. All of this is sitting on the heap and never goes out of scope. The only thing that could go out of scope is the pointer that points to the root of the list - the pointer you use to reference into the list in the first place. That can go out of scope.

这是在堆上创建东西的示例,并且根指针超出范围:

Here's an example of creating stuff on the heap, and the root pointer going out of scope:

if (myCondition) {
    Node* list_1 = new Node (3);
    Node* list_2 = new Node (4);
    Node* list_3 = new Node (5);

    list_1->next = list_2;
    list_2->next = list_3;
    list_3->next = null;
}
// The list still exists
// However list_1 just went out of scope
// So the list is "marooned" as a memory leak

相关文章