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

C++ 指向同一内存位置并删除其中一个的两个指针,c++,C++,我有两个不同的类,在两个不同的线程中访问指向另一个类的指针。 在第一个线程中,根据一些用户输入删除该指针,secodn线程仍然尝试访问该内存位置,应用程序在该点崩溃。 两个线程之间共享的所有数据结构都受到所需锁的保护,并且不存在争用条件。 我在这里省略了有关锁等的细节 线程1: class UD { public: char* GetName(); //returns name of UD entry private: //some data char* name; } stru

我有两个不同的类,在两个不同的线程中访问指向另一个类的指针。 在第一个线程中,根据一些用户输入删除该指针,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已经多次添加到Drawlist中,并且一些draw节点仍然有一个指向该UD的ptr。 //UD上的所有这些删除、添加和呈现都是通过互斥保护完成的,因此不存在竞争条件

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

在绘制中,在渲染之前,我正在检查, if(Item::mpUd)--//这永远不是0 { mpUd->GetName()//等等,访问它的方法。这里有一个崩溃 }

我已经看到了一个类似问题的答案:

所以,我知道我做错了。
任何建议,我如何才能正确地实施这种行为,我强调,我不能改为共享ptr,共享ptr给你的是引用计数。因此,您可以创建一个共享的_ptr,然后复制它来创建另一个,并通过这些来访问您的内存。当您删除任一共享_ptr时,析构函数不会释放内存。它仅在最后一个共享的\u ptr被销毁时执行此操作

如果您(以某种方式)设法获得指向同一内存的两个独立共享\u ptr,共享\u ptr对您没有帮助-但这永远不会发生,因为共享\u ptr中的内存始终由共享\u ptr分配,您对指针的内存拥有很强的所有权(您不应该把它看作普通的C指针,而应该把它看作共享的_ptr对象的内容,就像任何其他对象都包含数据一样)


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

无垃圾收集器r、 由程序员来管理对象的生命周期。有一些模式使这项工作更容易,而智能指针使实现其中一些模式更容易

您当前有两个对象,我们将它们称为
A
B
,而
B
负责
删除
-ing
A
中的某个对象
A
的某些
A
的整个生命周期内都有效,但
A
的某些对象受
B
的生命周期的限制。今天这是缺点人们普遍认为让
B
了解
a
的内部结构总是一个坏主意(),虽然我认为这是一个很好的经验法则,但你不必为了遵循它而重新设计一切

对于您现在遇到的问题,最好的答案是停止让一个对象
删除另一个对象内部的某个对象。让每个对象负责管理其自身内部的生命周期。从您发布的代码来看,
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

这就是智能指针被发明的原因。如果这不是你的选择,我祝你好运。为什么另一个指针会变为空?这样想,你有两个不同的变量恰好有相同的值(它们指向相同的地址)。将其中一个设置为NULL不会更改另一个的值。您可以将其向后设置。设置为NULL的不是内存位置,而是指针本身。想象一下:
int x=42;int y=x;x=3;
y
值是多少?
42
。显然,指针也会出现同样的情况:
int array[]={42,3};int*x=array[0];int*y=x;x=array[1];
y的值是多少?
array[0]
,因此
y
仍然指向这个地址上的任何东西(这里正好是
42
)。你不删除指针。你删除对象。指针只是指向它们。是的,这是有效的,但我之前没有看到这条消息,现在,我按照第一个答案中的建议并实现引用计数。即使使用GC,也要由程序员来管理对象的生存时间。GC提供的是一种只管理内存的简单方法。我如果您的对象包含非内存资源,您将回到手动内存管理(例如在.NET程序中到处使用和IDispose,在Java中尝试/最终)。甚至Microsoft也必须实现SafeHandle来处理一些对象资源-仅供参考,这是一个引用计数的包装器。谢谢,按照建议添加引用计数器
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

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

    return false;
}
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