试图弄清楚何时调用析构函数
Trying to figure out when destructors get called
下面我有一个保持引用计数的类和一个封装指向另一个对象的指针的类。
当类Ptr
不再附加任何对象时,我希望解除分配。这需要删除对象和引用计数。当ptrcnt
的值达到零时,会发出信号。
我的Ptr_count
类有一个析构函数,它完成自己的部分释放。我知道当Ptr
的析构函数被调用时,这个析构函数会被调用,它会相应地释放内存。然而,当调用Ptr
的赋值运算符并执行代码if(--refptr == 0) { delete p; }
时,我不太确定它是否会被调用。
Ptr_count
中有一段代码被注释掉了。如果我使用它而不是析构函数,那么每当refptr
的值变为零时,就会发生释放。
我的问题是,有没有一种方法可以在Ptr
中的赋值操作期间调用析构函数,或者我需要使用Ptr_count
中注释掉的代码来获得正确的内存释放?
很明显,当我退出程序时,析构函数会被调用,内存会以某种方式被释放,但当程序运行时,我认为在这种情况下,引用指针即使在达到零之后也可以继续递减,内存仍然存在。
class Ptr_count {
public:
Ptr_count() : ptrcnt(new size_t(1)) { }
~Ptr_count()
{
if(ptrcnt && *ptrcnt <= 0)
delete ptrcnt;
}
size_t operator++() const
{
++(*ptrcnt);
return *ptrcnt;
}
size_t operator--() const
{
--(*ptrcnt);
/*
if(*ptrcnt == 0) {
delete ptrcnt;
return 0;
}
*/
if(ptrcnt)
return *ptrcnt;
else
return 0;
}
operator bool() const
{
return ptrcnt;
}
size_t operator*() const
{
return *ptrcnt;
}
private:
size_t* ptrcnt;
};
template <class T> class Ptr {
public:
Ptr() : p(0) {}
Ptr(T* t) : p(t) {}
Ptr(const Ptr& h) : p(h.p), refptr(h.refptr) { ++refptr; }
Ptr& operator=(const Ptr& rhs)
{
++(rhs.refptr);
if(--refptr == 0) {
delete p;
}
refptr = rhs.refptr;
p = rhs.p;
return *this;
}
~Ptr()
{
if(--refptr == 0) {
delete p;
}
}
operator bool() const { return p; }
private:
T* p;
Ptr_count refptr;
};
编辑::
或者,如果类Ptr_count
有自己的赋值运算符,这会解决问题吗?如果我将下面的代码添加到Ptr_count
,那么在分配期间引用计数达到0时,我似乎可以释放内存。
void operator=(const Ptr_count& rhs)
{
if(ptrcnt == 0)
delete ptrcnt;
ptrcnt = rhs.ptrcnt;
}
首先,如果这是为了自学,请继续。否则,请停止您正在做的工作,开始使用std::shared_ptr
/std::unique_ptr
/std::weak_ptr
,或者如果您不能使用C++11 std::auto_ptr
。
现在转到您的代码:
1)在复制构造函数Ptr_count
而不是Ptr
的复制构造函数中增加引用计数会更安全、更自然,因为Ptr_count
类的目的是管理引用计数。
这样做之后,您可以完全删除Ptr
的复制构造函数
2)在Ptr
:的赋值运算符中存在不必要的检查
// Counter *must* be greater than 0 here, else p is 0 anyways.
Ptr& Ptr::operator=(const Ptr& rhs)
{
++(rhs.refptr); // Increment your counter to 2 or above.
if(--refptr == 0) { // Decrement your counter to 1 or above.
delete p; // Never get here.
}
refptr = rhs.refptr;
p = rhs.p;
return *this;
}
3)您最大的问题是覆盖赋值运算符中的refptr
和p
。
Ptr& operator=(Ptr const& rhs)
{
Ptr temp(rhs);
std::swap(refptr, temp.refptr);
std::swap(p, temp.p);
return *this;
}
应该解决这个问题。
4)您的Ptr_count
递减运算符有点坏了。
size_t Ptr_count::operator--() const
{
--(*ptrcnt); // Access address stored in ptrcnt.
if(ptrcnt) // Test if address is valid.
return *ptrcnt;
else
return 0;
}
如果调用此方法时ptrcnt
为0,则会因为--(*ptrcnt)
而导致访问冲突。无论如何,这应该没有必要,只需删除它:
size_t Ptr_count::operator--() const
{
return --(*ptrcnt);
}
tl;dr因为代码包含1000多个单词,所以完整的代码:
class Ptr_count {
public:
Ptr_count() : ptrcnt(new size_t(1)) { }
Ptr_count(Ptr_count const& rhs) : ptrcnt(rhs.ptrcnt) { ++(*this); }
~Ptr_count()
{
if(ptrcnt && *ptrcnt <= 0)
delete ptrcnt;
}
size_t operator++()
{
return ++(*ptrcnt);
}
size_t operator--()
{
return --(*ptrcnt);
}
operator bool() const
{
return ptrcnt;
}
size_t operator*() const
{
return *ptrcnt;
}
private:
size_t* ptrcnt;
};
template <class T> class Ptr {
public:
Ptr() : p(0) {}
Ptr(T* t) : p(t) {}
Ptr& operator=(Ptr const& rhs)
{
Ptr temp(rhs);
std::swap(refptr, temp.refptr);
std::swap(p, temp.p);
return *this;
}
~Ptr()
{
if(--refptr == 0)
delete p;
}
operator bool() const { return p; }
private:
T* p;
Ptr_count refptr;
};
我认为你的问题主要归结为:这个赋值调用了计数器的析构函数吗?
refptr = rhs.refptr;
答案是否定的。就我个人而言,我倾向于实际上而不是将计数器代码封装到一个单独的类中,而是直接在Ptr
类中完成。此外,我认为我实现赋值运算符的规范方法将处理正确的行为:
Ptr& Ptr::operator(Ptr other) {
this->swap(other);
return *this;
}
void Ptr::swap(Ptr& other) {
std::swap(this->p, other.p);
this->ptrcnt.swap(other.ptrcnt);
}
void Ptr_count::swap(Ptr_count& other) {
std::swap(this->ptrcnt, other.ptrcnt);
}
也就是说,尽管引用计数指针的简单实现是一个有趣的面试问题,但我强烈建议永远不要真正实现引用计数指针[除非你碰巧也实现了标准C++库的其余部分],只使用std::shared_ptr<T>
:除了已经制定出如何管理计数的细节之外,这个类实现了一些非常酷的功能,这些功能远远超出了简单的引用计数指针,而且这些功能中的许多在实际代码中都是需要的。
- 什么时候调用析构函数
- C++-明确何时以及如何调用析构函数
- C++ 防止在映射中放置()时调用析构函数
- 调用析构函数以释放动态分配的内存
- C++:使用方法调用析构函数的顺序是什么?
- 向量推回调用析构函数时调用析构函数
- 如何在调用析构函数时优雅地停止/销毁带有阻塞调用C++线程?
- C++,我应该调用析构函数吗?
- 如何获取有关在 Clang LibTooling 中调用析构函数的信息?
- 当我从 std::vector 中的新放置调用析构函数时会发生什么?
- 为什么这里不调用析构函数
- 在调用 std::bind 的产品后意外调用析构函数
- 为什么在传递给函数而不是构造函数时调用析构函数?
- 如何在C++中调用析构函数
- 为什么为未删除的对象调用析构函数?
- 调用析构函数时出错
- C++ 在不释放内存的情况下调用析构函数
- 为什么在运算符删除中不调用析构函数?
- C++ 调用析构函数后动态模板队列"double free or corruption (out)"
- 在 postOrderDelete 上调用析构函数时引发的异常