如何在此方案中使用new和delete

时间:2016-08-11 09:58:01

标签: c++ pointers hash binary-search-tree

所以我有一个类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(删除各自的节点)但是如何确保aVendoraCollectionaCollection中创建的内容只删除一次吗?

P.S。是否需要在aCollection中调用new?我认为指针需要保持分配状态,因此数据始终存在。

代码有点冗长,所以我快速说明了发生了什么。 Illustration of program add function

---- ----解决方案

感谢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 { }

1 个答案:

答案 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表现一样可怕......否则在生产代码中,坚持使用标准容器并围绕它们设计实现通常效率更高,因为它们具有相当好的性能,并且您不必实现它们,只能使用它。

(1)完全避免新/删除。如果这种方式对您不实用(aVendor默认构造函数成本很高),请在插入/删除供应商时使用std::vector<aVendor *> vendors;和手动new / delete

编辑:

&#34;如何确保aCollection中创建的aVendor只被删除一次?&#34;

好吧,你只需在aCollection中删除一次。

问题不明确,您的问题是什么(当您在所有节点中删除供应商时,您可能正在努力检测,并且您想要从aCollection发布它? #39;完全不同的问题,需要更多的架构洞察应用算法,看看是否有一些好的地方可以检测到&#34;悬空&#34;供应商不再被任何节点使用,触发在aCollection)中删除。

编辑:如何新建/删除示例:

live example

#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符合您的目的,首先必须知道目的。

因此,仅将此作为newdelete所属的示例。 (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,只是更短更快,而且你会隐含地知道特定数据的发布何时发生(当它们超出其范围时)。