所以我有一个类aCollection
,它的成员是二进制搜索树和哈希表(用于按不同参数组织数据)。我设计程序的方式是aCollection
有一个add(aVendor& vendor)
函数,它接受在main中创建的虚拟供应商对象,并生成指向供应商对象的指针(使用new),然后传递给该对象到bst和哈希表的add
函数。
在bst和hash表中,他们使用new创建一个节点,该节点包含指向供应商对象的指针和必需的链接指针(下一个,左,右等)。
总之,伪数据对象转到aCollection::add(aVendor& vendor)
,并且指向aVendor
对象(其中包含数据)的指针被发送到bst和hash表,然后将该指针存储在自己的他们使用node
声明的new
个对象。
我的问题是,我应该如何使用delete
来正确释放内存? bst和hash表共享指向传递给它们的aVendor
对象的指针并且它们每个都有自己的节点要删除。 我知道我需要在bst和哈希表的delete
函数中调用remove
(删除各自的节点)但是如何确保aVendor
为aCollection
在aCollection
中创建的内容只删除一次吗?
P.S。是否需要在aCollection
中调用new?我认为指针需要保持分配状态,因此数据始终存在。
---- ----解决方案
感谢Ped7g的出色解释,我发现由于std::list
应该是删除指针的函数,我需要跟踪要删除的指针。按照他/她的建议,我决定使用delete
将添加到程序中的所有指针添加到列表中,然后我在析构函数中设计了一个while循环,遍历这些指针并aCollection
s它们,从而防止源于 20 //Constructor function
21
22 aCollection::aCollection()
23 {
24 //allocates one instance of a hash table and bst
25 hashTable = new hashFunctions;
26 bst = new aBst;
27
28 //Creates a list to track ptrs
29 trackList = std::list<aVendor*>();
30 return;
31 }
32
33 //Destructor
34 aCollection::~aCollection()
35 {
36 //Destroys hashTable and bst
37 delete hashTable;
38 delete bst;
39 //Deletes vendor pointer objects
40 while(!trackList.empty())
41 {
42 delete trackList.front();
43 trackList.pop_front();
44 }
45 return;
46 }
的内存泄漏,这是我写的代码。
84 trackList.push_front(vendorPtr);
在add函数中,我使用这行代码将指针添加到列表
aCollection.h
最后,这就是我将列表声明为 43 list<aVendor*> trackList;
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from 'app/components/app';
@NgModule({
imports: [BrowserModule, FormsModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule { }
答案 0 :(得分:2)
尝试遵循&#34;一切都属于某个地方&#34;原理
只有信息的所有者处理new
/ delete
(更好地说&#34;尽可能避免新/删除&#34;),其他主题不在乎,如果他们收到一个指针,他们会认为它在整个处理过程中都是活的 - 因为这是所有者的责任,如果它指出了指针,它应该知道它将如何[长]在外面使用,并调整它的新/删除策略来满足这些需求。所以他们不会删除它。
在您的情况下,它取决于很多,您是否经常从结构中删除供应商,或者您只是添加供应商。如果您绝对很少删除供应商并且允许大量性能损失,那么您可以在aCollection中使用std::vector<aVendor> vendors;
(1),将bs和hash节点仅给予供应商迭代器(或指针/索引)。如果从向量中删除供应商,则所有bs / hash节点必须使用新的迭代器(指针/索引)更新,删除后的性能损失。好吧,实际插入供应商也会使迭代器和指针无效,只有索引才能生存,所以使用索引 - 这也很清楚,bs / hash节点如何关心供应商的delete
(你不要删除索引,毫无意义)。
如果经常删除供应商,std::list
是更好的选择,因为插入/删除不会使迭代器无效,因此bs / hash节点中迭代器的所有副本都将保持正确。
总的来说,看起来你编写了自己的std::*
容器实现......有什么特别的原因吗? (这是一个很好的学习练习,在非常罕见的情况下是表现决定,但在这种情况下,你会以完全不同的设计结束,因为你所拥有的就像std::list
表现一样可怕......否则在生产代码中,坚持使用标准容器并围绕它们设计实现通常效率更高,因为它们具有相当好的性能,并且您不必实现它们,只能使用它。/ p>
(1)完全避免新/删除。如果这种方式对您不实用(aVendor默认构造函数成本很高),请在插入/删除供应商时使用std::vector<aVendor *> vendors;
和手动new
/ delete
。
编辑:
&#34;如何确保aCollection中创建的aVendor只被删除一次?&#34;
好吧,你只需在aCollection
中删除一次。
问题不明确,您的问题是什么(当您在所有节点中删除供应商时,您可能正在努力检测,并且您想要从aCollection
发布它? #39;完全不同的问题,需要更多的架构洞察应用算法,看看是否有一些好的地方可以检测到&#34;悬空&#34;供应商不再被任何节点使用,触发在aCollection
)中删除。
编辑:如何新建/删除示例:
#include <iostream>
#include <list>
#include <string>
class Vendor {
private:
std::string name;
public:
Vendor(const std::string & name) : name(name) {}
const std::string & getName() const { return name; }
};
// A bit pointless example how to handle naked new/delete.
class VendorList {
private:
std::list<Vendor *> vendors;
// usually with neatly designed classes the std::list<Vendor>
// would suffice, saving all the hassle with new/delete
// (that's why this example is a bit pointless)
// Also storing around iterators to internal list outside
// of VendorList class feels quite wrong, that's a code smell.
public:
~VendorList() {
std::cout << "~VendorList() destructor called.\n";
// release any remaining vendors from heap
auto vendorIterator = vendors.begin();
while (vendorIterator != vendors.end()) {
auto toRemove = vendorIterator++;
removeVendor(toRemove);
}
// release the (now invalid) pointers
vendors.clear();
// at this point, whoever still holds iterator
// to a vendor has a problem, it's invalid now.
}
// stores vendor into collection of vendors
// + data of vendor are allocated on heap by "new"
// returns iterator pointing to the newly added vendor
// (not a best choice for public API)
std::list<Vendor *>::iterator addVendor(const Vendor & vendor) {
Vendor * copyOnHeap = new Vendor(vendor);
std::cout << "VendorList: adding vendor: "
<< copyOnHeap->getName() << std::endl;
return vendors.insert(vendors.end(), copyOnHeap);
}
// removes particular vendor from the list
// to be used after the rest of application does not hold any iterator
void removeVendor(std::list<Vendor *>::iterator vendor_iterator) {
std::cout << "VendorList: releasing specific vendor: " <<
(*vendor_iterator)->getName() << std::endl;
// release the heap memory containing vendor's data
delete *vendor_iterator;
// remove the released pointer from list
vendors.erase(vendor_iterator);
// at this point, whoever still holds iterator
// to that vendor has a problem, it's invalid now.
}
const std::list<Vendor *> & get() const {
return vendors;
}
};
int main()
{
VendorList vlist;
vlist.addVendor(Vendor("v1"));
auto v2iterator = vlist.addVendor(Vendor("v2"));
vlist.removeVendor(v2iterator);
for (auto vendorPtr : vlist.get()) {
std::cout << "Vendor in list: " << vendorPtr->getName() << std::endl;
}
}
我对这个例子并不高兴,因为它在许多层面上感觉不对(比如糟糕的OOP设计),但为了使API符合您的目的,首先必须知道目的。
因此,仅将此作为new
和delete
所属的示例。 (VendorList
中只有一次,是所有者+负责管理这些内容。应用中的其他任何人都不应在供应商上使用new
/ delete
,而是应该调用VendorList
添加/ remove函数,让列表来管理实现细节(比如它的存储位置,以及每个供应商使用的新/删除数量)。
通常通过设计精简数据类并避免使用裸指针,可以避免在C ++代码中完全删除/删除。您可以尝试将此示例转换为std::list<Vendor>
变体,代码将被简化很多(空的析构函数代码,默认情况下将释放列表),仅在列表中调用的删除/插入等。
然后按范围处理内存中数据的生命周期。与main
中的VendorList vl;
一样,因为供应商列表的实例将在应用程序的整个生命周期中使用。或者,如果您只在发票处理期间需要它,您可以在processInvoices()
内声明它,再次作为局部变量,初始化它,然后忘记它。当你超出processInvoices()
范围时,它会被释放。
将释放所有已初始化的供应商,因为它们属于VendorList。等。
所以只要你设法用明确的#34设计你的课程,属于&#34;和&#34;负责&#34;,你可以通过仅使用本地/成员变量,而不是使用new / delete / shared_ptr / etc ...来获得很大的帮助。源代码看起来几乎就像带有GC的Java,只是更短更快,而且你会隐含地知道特定数据的发布何时发生(当它们超出其范围时)。