两个指针指向同一内存位置并删除其中一个

Two pointers pointing to same memory location and deleting one of those

本文关键字:删除 一个 位置 两个 指针 内存      更新时间:2023-10-16

我在两个不同的线程中有两个不同类访问指向另一个类的指针。在第一个线程中,这个指针根据一些用户输入被删除,而secodn线程仍然试图访问那个内存位置,此时应用程序崩溃。两个线程之间共享的所有数据结构都受到所需锁的保护,并且不存在竞争条件。我在这里省略了与锁等相关的细节。

威胁1:

class UD
{
public:
char* GetName(); //returns name of UD entry
private:
    //some data
char* name;
}

struct UDentry 
{
        class UD*       mpUd;
        //and other required member variables
};
class ABC
{
    --usual functions
    UD* getUD(const char* name);
    private:
        std::vector<struct UDentry> mUDs;
};
UD* ABC::getUD(const char* name)
{
    if((idx = GetIndex(name)) >= 0)
    {
        return(mUDs[idx].mpUd);
    }
    else
    {
        // o/w create UD and return it
    //............... Create UD, say localUd
    //and add it to mUDs
        if((idx = GetIndex(pFontName)) >= 0)
        {
            return(mUDs[idx].mpUd);
        }       
    }
}

//有一些代码可以确保只有一个具有给定名称的UD被添加到列表中。我现在无法更改数据结构,尽管我知道它不是正确的数据结构。

int ABC::GetIndex(const char * pName) const
{
    for(int i = 0; i < mUDs.size(); ++i)
    {
        if((stricmp(mUDs[i].mpUd->GetName(), pName) == 0))
            return i;
    }
    return(-1);
}

//在另一个线程中,有一个名为DrawList的列表。//它包含指向UD的指针,并使用它,并根据UD 中的信息在屏幕上绘制一些内容

线程2:

class Item
{
public: 
    Item(char* name, //other args);
private:
    UD* mpUd;
}
Item::Item(char* name, //other args):
mpUd(getUD(name))
{
}
//Stores draw commands
class DrawList
{
public:
    DrawList();
    ~DrawList();
    void AddItem(Item *pItem);
    //method to draw text on screen based on info in UD 
    void Draw();
private:
    DrawList *      mpHead;                     //!< Pointer to the start of the list of blocks containing rendering commands (or NULL if empty)
    DrawList *      mpTail;                     //!< Pointer to the last addtion to the list of block of rendering commands (for easy & fast apends)
    //and some other class specific methods and variables
};

class Render
{
private:
//contains a ptr to DrawList class, another boost::shared_ptr to Drawlist and a mutex to access list
//This class sits in a tight loop, 
}
void DrawList::Draw()
{
        //takes each item in turn and displays some text on-screen based on info in UD
}

所有这些都是遗留代码,所以我不能选择将ptr更改为智能ptr等,庞大的代码库

我为这个代码添加了一些新功能。现在,在创建UD的第一个线程中,根据一些用户操作,我们可能不得不删除UD

//new methods
bool ABC::deleteUD()
{
    if((idx = GetIndex(pFontName)) >= 0)
    {
        delete mUDs[idx].mpUd;
        mUDs[idx].mpUd = 0;
        return true;
    }
    return false;
}

//现在,在第二个线程中,这个UD已经被多次添加到Drawlist中,并且一些绘制节点仍然有一个指向那个UD的ptr。//UD上的所有这些删除、添加和呈现都是通过互斥锁保护完成的,因此不存在竞争条件。

我曾假设,如果我删除了ABC类中的UD并使其为Null,那么指针(项目::mpUd(也将变为Null。但那个指针仍然指向那个地址,那个地址空间的内存要么被重新使用,要么包含垃圾值(这很令人惊讶,因为我以为我把它设为Null(。所以,当在Draw方法中,我试图访问它时,程序崩溃了。

在Draw中,在渲染之前,我正在检查,if(项目::mpUd(///这永远不是0{mpUd->GetName((//等,访问其方法。我撞车了}

我看过一个类似问题的答案:当有两个指针指向相同的内存位置时,C++删除一个指针

所以,我知道我所做的是错误的。任何建议,我如何正确地实现这种行为,我强调,我不能更改为共享ptrs

shared_ptr为您提供的是引用计数。所以你创建了一个shared_ptr,然后复制它来创建另一个,并通过这些访问你的内存。删除shared_ptr时,析构函数不会释放内存。只有当最后一个shared_ptr被销毁时,它才会执行此操作。

如果你(以某种方式(设法让两个独立的shared_ptr指向同一内存,shared_ptrs对你没有帮助,但这永远不会发生,你对指针的内存有很强的所有权(你不应该把它看作普通的C指针,而应该把它更多地看作shared_ptr对象的内容,就像任何其他对象包含数据一样(。

因此,您可以在外部实现同样的行为。每次初始化指向共享内存区域的指针时,都会增加一个引用计数器。每次"释放"它时,都会递减引用计数器。当引用计数器递减到0时,然后,也只有这样,才能释放内存。

如果没有垃圾收集器,就由程序员来管理对象的生存期。有一些模式可以让这项工作变得更容易,而智能指针可以让实现其中一些模式变得更容易。

您目前有两个对象,我们将它们称为AB,而B负责delete—在A中执行某些操作A的某些A的所有生存期都有效,但A的某些受B的生存期限制。今天,这被认为是一个坏主意,而且几乎不可能把它做好。人们普遍认为,让B了解A的内部总是一个坏主意(德米特定律(,虽然我认为这是一个很好的经验法则,但你不必重新设计所有东西来遵循它

对于你现在遇到的问题,最好的答案是简单地停止一个对象delete——另一个对象内部的东西。让每个对象负责管理其内部的生命周期。从您发布的代码来看,B没有理由仅仅因为不想再使用指针而调用delete。相反,您可以将B NULL从指针的副本中取出,同时保留原始对象供其他对象(例如C(使用。正如您所了解的,NULL使用一个指针不会影响其他指向同一事物的指针:

int i = 5;
int* ip = &i;
int* ip2 = &i;
int* ip3 = ip;
int* ip4 = ip2;
ip = NULL; // has no effect on ip2, ip3 or ip4