我在两个不同的线程中有两个不同的类访问指向另一个类的指针。 在第一个线程中,基于某些用户输入删除此指针,并且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
}
所有这些都是遗留代码,因此我没有选择将ptrs转换为智能ptrs等广泛的代码库
我为此代码添加了一些新功能。 现在,在创建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。 //所有这些删除,添加和渲染都是通过互斥保护完成的,因此没有竞争条件。
我曾经假设,如果我在ABC类中删除UD并使其为Null,那么指针(Item :: mpUd)也将变为Null。 但是该指针仍然指向该地址,并且该地址空间的内存已被重用或者包含垃圾值(这是令人惊讶的,我认为我已经将其设为Null)。所以,在Draw方法中,我尝试访问它,程序崩溃。
在Draw中,在渲染之前,我正在检查, if(Item :: mpUd) - //这永远不会是0 { mpUd-&gt; GetName()//等,访问其方法。我在这里遇到了崩溃 }
我看了一个类似问题的答案: C++ deleting a pointer when there are 2 pointers pointing to the same memory locations
所以,我知道我所做的是错的。 任何建议,我如何正确实现这种行为,我强调,我不能改为共享ptrs
答案 0 :(得分:2)
shared_ptr给你的是引用计数。因此,您创建一个shared_ptr,然后复制它以创建另一个并通过这些访问您的内存。删除shared_ptr时,析构函数不会释放内存。它只在最后一个shared_ptr被销毁时执行此操作。
如果你(以某种方式)设法让两个独立的shared_ptrs指向同一个内存,那么shared_ptrs不会帮助你 - 但这种情况永远不会发生,因为shared_ptr中的内存总是由shared_ptr分配,你拥有强大的指针所有权&# 39; s内存(你不应该用正常的C指针来考虑它,把它想象成shared_ptr对象的内容,就像任何其他对象包含数据一样)。
因此,您可以在外部实现相同的行为。每次初始化指向共享内存区域的指针时,都会增加引用计数器。每次你都免费&#39;它改为减少参考计数器。当参考计数器减少到0时,那么,只有那时,你是否释放了内存。
答案 1 :(得分:0)
没有垃圾收集器,它可以由程序员来管理对象的生命周期。有一些模式可以使这项工作更容易,而智能指针可以更容易地实现其中一些模式。
您目前有两个对象,我们称之为A
和B
,而B
负责delete
A
中的内容}。 A
的部分对A
的所有生命周期有效,但部分A
受B
&#39限制一生。今天,这被认为是一个坏主意,并且几乎不可能做到正确。人们普遍认为让B
知道A
的内部结构总是一个坏主意(Law of Demeter),而我认为这是一个很好的事情。经验法则,你不必重新设计所有内容来跟随它。
您现在遇到的问题的最佳答案就是停止在另一个对象内部放置一个对象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