It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened,
visit the help center。
9年前关闭。
我在C++中使用类和嵌套类进行工作。我确实有很多类和嵌套类。我不知道我什么时候应该释放内存。是否有软件向您显示在哪里或何时从类中取消分配指针?
谁能帮我?
谢谢
我想提一下,我在ubuntu下使用C++进行工作。我的文件是.cpp和.h文件。没有主线。
我正在使用类* x = new class()。我没有删除x是因为我不知道;我不知道什么时候应该删除它。
对OP:您的评论表明对C++的工作方式有些困惑。您需要了解一些重要的事情,以了解一些事情:
处理指针本质上是危险的。错误地使用指针可能会引入未定义的行为,这不仅是程序中的错误,而且编译器可能无法检测到该错误,甚至没有提供任何帮助,并且在运行时没有告诉您程序将如何运行。行为:它可能立即崩溃,但也可能继续运行,但会导致程序的另一部分(正确率100%正确)崩溃或行为与应有的不同。没有确定性地检测这些情况的自动方法,因此良好的设计至关重要。 指向对象的指针与其指向的对象是不同的东西。附带说明一下,如果您来自Java之类的语言,则在C++中,“对象”一词也指内置类型的变量,例如int,double等。类型X的指针是一个值类型,它可以或可以不指向类型X的对象。除了具有特殊值0(NULL)之外,它还可以简单地指向内存中不包含任何随机值的位置。类型为X的对象。此类指针称为“悬挂指针”。 访问指针所指向的对象的行为称为取消对指针的引用。取消引用悬空指针或空指针是未定义行为。此外,虽然程序可以检查指针是否为空,但无法检查它是否为悬空指针。以这样的方式构造程序是程序员的责任,即永远不要取消悬空指针的引用。 在您的一个问题中,您询问有关创建指针的问题。创建指针绝对不意味着您必须在其上调用delete
,因为创建指针与创建指针将指向的对象不同。考虑以下代码,该代码创建一个指针:SomeType* pointer;
尽管实际上确实创建了一个指针,但它是一个悬空的指针,如果尝试在其上调用delete
,则会得到未定义的行为。 内存分配的问题与这样的指针无关,例如与对象相关的对象,为之分配或取消分配内存。通常,您不会在C++中显式分配或取消分配内存,而是创建和销毁对象,并为它们隐式分配内存。 new
运算符不创建指针,它创建动态对象,为它们分配所需的内存,并返回指向新创建的对象的指针。动态分配的对象的生命周期将持续存在,并且它们将继续占用它们所占用的内存,直到您在指针上调用delete
为止,这会破坏动态对象(而不是指针本身)并释放其占用的内存。如果您反复分配动态对象,但在停止使用它们后仍无法删除它们,则内存中将充满未使用的对象:这称为内存泄漏。 指针不仅可以指向动态分配的对象,还可以指向其他类型的对象:局部变量,结构或类成员,数组元素。就像无法判断指针是否悬空一样,也无法判断指针是否指向动态对象。尝试在指向动态对象以外的其他任何有效指针上调用delete
会导致未定义行为。 可能有许多指针指向同一对象。如果对象被破坏,则在动态对象的情况下通过调用delete或以任何其他方式将其指向的所有指针变为悬空指针。 指针变量或类/结构成员本身就是对象,但它们的生存期并不与它们指向的任何对象相关:就像创建指针时未创建指向对象一样,该对象指向(如果有)销毁指针时不销毁)。这样就可以安全地破坏指向同一对象的多个指针,或者破坏悬挂的指针。 使用
new
创建动态对象并使用
delete
销毁动态对象时,还需要考虑的另一个问题是异常安全性:在许多情况下,最初看起来像对
delete
创建的所有对象进行
new
的代码可能不会这样做,因为会引发异常。例如:
{
SomeType *p = new Sometype
// some code
delete p;
}
在这种情况下,如果“某些代码”引发异常,则永远不会到达
delete p
,并且会发生内存泄漏。傻傻地:
class A {
SomeType *p;
// more members
public:
A(): p(new SomeType()) /* more intializers */ { /* body */ }
~A() { delete p; }
};
在这种情况下,如果在初始化p之后A构造函数引发异常,则析构函数将永远不会被调用,并且您将发生内存泄漏。
考虑到这些缺陷,存在几种管理这些问题的设计技术。
除非必要,否则不要使用指针和/或动态对象:在许多情况下,无需动态分配对象就可以实现。对于局部变量,变量的生命周期仅限于函数范围,您可以将对象定义为自动变量,因此可以代替:
{
SomeType* p = new SomeType();
// do something with *p
delete p;
}
你做:
{
SomeType var;
// do something with var
} // var destroyed here
在第二种情况下,var超出范围时将被销毁,并且将释放其内存-即使抛出异常。
同样,对于包含其他对象的对象,可以直接将该对象作为成员包括在内,因此可以代替:
class Containing {
Contained* member;
};
你可以做
class Containing {
Contained member;
};
在这种情况下,包含成员的内存在包含对象内部,并且在释放包含对象时将释放该内存,此外,如果在包含对象的构造过程中初始化了包含成员,但是包含构造函数随后抛出异常,则可以保证即使包含对象的析构函数没有被调用,所包含的成员也将被正确地销毁,包括调用其自己的析构函数。
由于不涉及任何指针,因此您不必担心指针悬空或为空,并且可以获得减少时间和内存开销的其他优点。
缺点是:数据结构中没有递归(因此对于列表,树等,您需要使用指针),多态性(指向SomeType的指针可能指向SomeType的对象或从SomeType派生的类型(类型为自动类型的变量) SomeType只能是该特定类型),并且没有延迟的初始化(这对于类数据成员尤其重要,类数据成员的构造函数参数必须始终在包含的类初始化器列表中给出,而指针可以随时(重新)分配) 。
在必须使用动态对象的地方,您需要依靠设计来确保删除动态对象。典型的模式是“所有者”的概念-是负责删除动态对象的实体。在这个概念中,每个动态对象都有一个并且只有一个“所有者”,它是当前正在执行的函数,或者是另一个拥有指向所拥有对象的指针的对象。因此,可能有许多指向一个对象的指针,但只有一个是拥有的指针。这在许多情况下适用:创建仅在该函数运行时才能生存的动态对象的函数将拥有该对象,这意味着该函数本身中必须存在执行删除的代码。在任何类型的分层数据结构(如树)中,“父”对象都可以拥有其“子代”。某些动态数据结构(例如链表)可以包装在拥有其动态节点并负责创建和删除它们的对象中,在本示例中,是将元素添加到列表或从列表中删除。
在更复杂的情况下,所有权可以在各个代码段之间转移。例如,当使用
insert
方法添加动态对象的集合时,它们可能会拥有在其外部创建的对象的所有权,而使用具有获取和删除语义(pop,dequeue ...)的
extract
方法来放弃该所有权。创建动态对象并返回指向它们的指针的函数可以将所有权传递给调用者,依此类推。
在对象保持动态对象所有权的情况下,您将始终要确保将它们在析构函数中删除,因为如果不发生这种情况,则当所有者被销毁时,所拥有的对象将保持没有所有者的状态,这意味着它将永远不会被删除。 ,实际上成为内存泄漏。所有者还可以在删除拥有的对象之前将其删除,这在许多情况下很有用。但需要注意的是:请确保所有者没有悬空的指针,认为该指针指向有效对象,然后尝试再次销毁它。在某些情况下,用新对象替换拥有对象时会销毁它,在这种情况下,您就不会遇到这个问题,因为拥有指针现在指向新拥有的对象,这也是有效的。在其他情况下,您可能希望将拥有指针设置为0,因此对象知道它不再拥有该指针下的任何内容(对null指针调用delete也很安全)。
在函数体或析构函数中使用手动
delete
来维护所有权存在两个问题:首先,此所有权未用代码表示,您必须对其进行单独管理,这很容易出错(因为编译器将且无法检测到)这里有任何错误)。我上面提到的异常问题使情况更加复杂:编写代码很难,而且容易出错,因此无论在何处引发异常,都将调用delete。
其他答案中提到的boost和standard库中的智能指针有助于以异常安全的方式显式表达所有权语义。 STL的
auto_ptr
和boost
scoped_ptr
具有上述严格的所有权语义,分别具有和不具有转移所有权的能力。如果您足够幸运可以使用当前的标准C++,则unique_ptr可以替代auto_ptr,从而可以解决一些问题。新标准STL,boost和TR1的
shared_ptr
具有“共享所有权”语义,其中一个对象具有多个“所有者”,并且在最后一个所有者放开它时将其销毁。使用这些智能指针类型中的每一个都有优点和警告,在使用它们之前,您应该先阅读这些优点和注意事项。