简体   繁体   English

内存泄漏 - STL设置

[英]Memory Leaks - STL sets

I am trying to plug up all my memory leaks (which is massive). 我试图堵塞我所有的内存泄漏(这是巨大的)。 I am new to STL. 我是STL的新手。 I have a class library where I have 3 sets. 我有一个类库,我有3套。 I am also creating a lot of memory with new in the library class for adding info to the sets... 我也在库类中创建了大量内存,用于向集合中添加信息...

Do I need to deallocate the sets? 我需要解除分配吗? If so, how? 如果是这样,怎么样?

Here is the library.h 这是library.h

    #pragma once

#include <ostream>
#include <map>
#include <set>
#include <string>
#include "Item.h"

using namespace std;

typedef set<Item*>              ItemSet;
typedef map<string,Item*>       ItemMap;
typedef map<string,ItemSet*>    ItemSetMap;

class Library
{

public:
    // general functions

    void addKeywordForItem(const Item* const item, const string& keyword);
    const ItemSet* itemsForKeyword(const string& keyword) const;
    void printItem(ostream& out, const Item* const item) const;

    // book-related functions

    const Item* addBook(const string& title, const string& author, int const nPages);
    const ItemSet* booksByAuthor(const string& author) const;
    const ItemSet* books() const;

    // music-related functions

    const Item* addMusicCD(const string& title, const string& band, const int nSongs);
    void addBandMember(const Item* const musicCD, const string& member);
    const ItemSet* musicByBand(const string& band) const;
    const ItemSet* musicByMusician(const string& musician) const;
    const ItemSet* musicCDs() const;

    // movie-related functions

    const Item* addMovieDVD(const string& title, const string& director, const int nScenes);
    void addCastMember(const Item* const movie, const string& member);
    const ItemSet* moviesByDirector(const string& director) const;
    const ItemSet* moviesByActor(const string& actor) const;
    const ItemSet* movies() const;
    ~Library();
};

I am not sure what i need to do for the destructor? 我不确定我需要为析构函数做些什么?

Library::~Library()
{


}

also, am I de allocating the stringset right? 另外,我正在分配字符串集吗?

    #ifndef CD_H
#define CD_H
#pragma once
#include "item.h"
#include <set>


typedef set<string> StringSet;


class CD : public Item
{
public:

    CD(const string& theTitle, const string& theBand, const int snumber);
    void addBandMember(const string& member);
    const int getNumber() const;
    const StringSet* getMusician() const;
    const string getBand() const;
    virtual void print(ostream& out) const;
    string printmusicians(const StringSet* musicians) const;

    ~CD();


private:

    string band;
    StringSet* music;

    string title;
    int number;

};

ostream& operator<<(ostream& out, const CD* cd);

#endif

cd.cpp cd.cpp

    #include "CD.h"

using namespace std;

CD::CD(const string& theTitle, const string& theBand, const int snumber)
: Item(theTitle), band(theBand),number(snumber), music(new StringSet)
{



}

CD::~CD()
{

    delete []music;

}

in the library class I am creating a lot of memory, but dont the destructor clean that up? 在库类中我创建了很多内存,但是析构函数不要清理它吗? example: 例:

    const Item* Library::addBook(const string& title, const string& author, const int nPages)
{

    ItemSet* obj = new ItemSet();
    Book* item = new Book(title,author,nPages);
    allBooks.insert(item); // add to set of all books
    obj->insert(item);

Note: I do not have a copy constructor. 注意:我没有复制构造函数。 I am not sure if I even need one or how top add one. 我不确定我是否需要一个或如何顶部添加一个。 I dont think my destructors are getting called either.. 我不认为我的析构函数也被称为..

STL containers are not designed to hold pointers. STL容器不是为了保持指针而设计的。

Look at the boost pointer containers. 看一下boost指针容器。 These containers are designed to hold pointers. 这些容器设计用于保持指针。

#include <boost/ptr_container/ptr_set.hpp>
#include <boost/ptr_container/ptr_map.hpp>

http://www.boost.org/doc/libs/1_42_0/libs/ptr_container/doc/ptr_set.html http://www.boost.org/doc/libs/1_42_0/libs/ptr_container/doc/ptr_set.html

The containers hold and own the pointers so they will be deleted when the container goes out of scope. 容器保存并拥有指针,以便在容器超出范围时删除它们。 But the beautiful thing about the containers is that you access the objects via references so all the standard algorithms work without any special adapters. 但是容器的美妙之处在于您通过引用访问对象,因此所有标准算法都可以在没有任何特殊适配器的情况下工作。

typedef boost::ptr_set<Item>              ItemSet;
typedef boost::ptr_map<string,Item>       ItemMap;
typedef boost::ptr_map<string,ItemSet>    ItemSetMap;

PS. PS。 Its hard to tell accurately but it looks like too many of your interfaces return pointers. 很难准确地说出来,但看起来有太多的接口返回指针。 It is very rare in C++ to actually return pointers (or pass pointers around). 在C ++中实际返回指针(或传递指针)是非常罕见的。 Your interfaces should usually take objects/references or smart pointers (usually in that order but it depends on the situation). 您的接口通常应该使用对象/引用或智能指针(通常按顺序,但这取决于具体情况)。

Working with a pointer should be your last resort as there is no clear indication of the owner of the object and thus cleanup becomes the issue (thus leading to massive memory leaks). 使用指针应该是你的最后手段,因为没有明确指示对象的所有者,因此清理成为问题(从而导致大量内存泄漏)。

You need to free the memory for each element of the set. 您需要为集合中的每个元素释放内存。 The container will not do that for you, and it shouldn't because it can't know whether it owns that data or not -- it could just be holding pointers to objects owned by something else. 容器不会为你做那个,它不应该因为它不知道它是否拥有该数据 - 它可能只是持有指向其他东西拥有的对象的指针。

This is a generic free function that will deallocate any STL container. 这是一个通用的免费函数,可以解除分配任何STL容器。

template <typename T>
void deallocate_container(T& c)
{
  for (typename T::iterator i = c.begin(); i != c.end(); ++i)
    delete *i;
}

// Usage
set<SomeType*> my_set;
deallocate_container(my_set);
my_set.clear();

haven't gone through all of your code but from the first few lines it seems you're maintaining sets of pointers. 没有完成你的所有代码,但从前几行开始,你似乎在维护一组指针。 Whenever you have an STL container which holds pointers and you're using new to put stuff in the pointers, you must use delete to deallocate these pointers. 每当你有一个保存指针的STL容器并且你使用new来将东西放入指针时,你必须使用delete来释放这些指针。 STL doesn't do that for you. STL不会为你做那件事。 In fact, STL doesn't even know they are pointers. 事实上,STL甚至不知道它们是指针。

Another option is to not use pointers at all and have a set of just the objects and not use new to create them. 另一个选择是根本不使用指针,只有一组对象,而不是使用new来创建它们。 Just create them on the stack and copy them into the set. 只需在堆栈上创建它们并将它们复制到集合中。

Well, this might be a stupid comment, but do you really need to have ALL your stuff heap-allocated (aka. using pointers and new ?) 好吧,这可能是一个愚蠢的评论,但你真的需要把所有你的东西堆分配(又名。使用指针和新?)

Can't you just use plain instances ? 你不能只使用普通的实例吗? RAII allows easier code and no memory leak. RAII允许更简单的代码,没有内存泄漏。

For example have : 例如:

using namespace std;

typedef set<Item>              ItemSet;
typedef map<string,Item>       ItemMap;
typedef map<string,ItemSet>    ItemSetMap;

class Library
{

public:
    // general functions

    void addKeywordForItem(const Item & item, const string& keyword);
    ItemSet itemsForKeyword(const string& keyword) const;
    void printItem(ostream& out, const Item & item) const;

    // book-related functions

    Item addBook(const string& title, const string& author, int nPages);
    ItemSet booksByAuthor(const string& author) const;
    ItemSet books() const;

    // music-related functions

    Item addMusicCD(const string& title, const string& band, int nSongs);
    void addBandMember(const Item & musicCD, const string& member);
    ItemSet musicByBand(const string& band) const;
    ItemSet musicByMusician(const string& musician) const;
    ItemSet musicCDs() const;

    // movie-related functions

    Item addMovieDVD(const string& title, const string& director, int nScenes);
    void addCastMember(const Item & movie, const string& member);
    ItemSet moviesByDirector(const string& director) const;
    ItemSet moviesByActor(const string& actor) const;
    ItemSet movies() const;
    ~Library();
};

With this approach, the destructor has to do strictly nothing, and no memory leak. 使用这种方法,析构函数必须严格执行任何操作,并且不会发生内存泄漏。 In most cases using pointers can be easily avoided, and definetly should! 在大多数情况下,使用指针很容易避免,而且应该是!

Looking at some of the code you posted in other questions (such as https://stackoverflow.com/questions/2376099/c-add-to-stl-set ), your items are stored in several global ItemSet objects. 查看您在其他问题中发布的一些代码(例如https://stackoverflow.com/questions/2376099/c-add-to-stl-set ),您的项目存储在多个全局ItemSet对象中。 This isn't a good design - they really should be part of the Library object, since they logically belong to that. 这不是一个好的设计 - 它们确实应该是Library对象的一部分,因为它们在逻辑上属于它。

The best way to fix the memory leaks is not to deal with raw pointers - either store smart pointers in the sets, or use Boost pointer containers as Martin York suggests. 修复内存泄漏的最好方法是不处理原始指针 - 要么在集合中存储智能指针,要么像Martin York建议的那样使用Boost指针容器。 Also, your ItemSetMap objects should contain Set objects rather than pointers - there is absolutely no reason to store pointers in them. 此外,您的ItemSetMap对象应该包含Set对象而不是指针 - 绝对没有理由在其中存储指针。

If you really must store pointers, then your destructor must walk through each set to delete the contents: 如果你真的必须存储指针,那么你的析构函数必须遍历每一组以删除内容:

void Purge(ItemSet &set)
{
    for (ItemSet::iterator it = set.begin(); it != set.end(); ++it)
        delete *it;
    set.clear(); // since we won't actually be destroying the container
}

void Purge(ItemSetMap &map)
{
    for (ItemSetMap::iterator it = map.begin(); it != map.end(); ++it)
        delete it->second;
    map.clear();
}

Library::~Library()
{
    Purge(allBooks);
    Purge(allCDs);
    // and so on and so forth
}

but this really isn't how you should be doing it, as just about everyone answering your questions has pointed out. 但这并不是你应该怎么做的,因为几乎每个回答你问题的人都指出了这一点。

As for the StringSet , you created it with plain new not new[] , so you must delete it with plain delete not delete[] . 对于StringSet ,您使用普通的new而不是new[]创建它,因此必须使用plain delete而不是delete[]删除它。 Or, better still, make music a StringSet object rather than a pointer, then you won't need the destructor at all. 或者,更好的是,将music StringSet对象而不是指针,那么根本不需要析构函数。 Once again, memory management through raw pointers and manual use of delete is error prone, and should be avoided if at all possible. 再次,通过原始指针和手动使用delete内存管理是容易出错的,如果可能的话应该避免。

In the destructor you need to iterate over your stl collections that contain pointers and delete them. 在析构函数中,您需要遍历包含指针的stl集合并删除它们。 Like this: 像这样:

while (!collection.empty()) {
    collection_type::iterator it = collection.begin();
    your_class* p = *it;
    collection.erase(it);
    delete p;
}

As others have noted, you need to deallocate the pointers. 正如其他人所说,你需要解除分配指针。 The set destructor normally wouldn't do this for you. set析构函数通常不会为您执行此操作。 Otherwise, if you want this to be done for you, use a boost::scoped_ptr or a std::tr1::shared_ptr where you can specify a custom deleter to do this job for you. 否则,如果您希望为此完成此操作,请使用boost::scoped_ptrstd::tr1::shared_ptr ,您可以在其中指定自定义删除器以执行此任务。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM