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