我一直在用c++编写自己的队列。问题在于它有一个名为Queue::Pop()的函数,它调用队列中第一个项的析构函数,这个析构函数由一个名为_First的索引跟踪,然后_First增加,_Length减少。通常编译时,它会执行预期的操作,但是当添加-O1标志时,它会开始执行奇怪的操作,并且不会正确地调用对象的析构函数。在析构函数中进行打印,但Id未设置为-1。因此,当队列超出作用域时,内存被delete[]编辑,析构函数再次被调用,这是无效的代码,因为如果它是文件描述符,它将被关闭两次。下面是Pop函数:
#include <iostream>
#include <tuple>
#include <memory>
#include <sys/uio.h>
#include <initializer_list>
namespace Core
{
namespace Iterable
{
template <typename T>
class Span
{
private:
size_t _Length = 0;
T *_Content = nullptr;
inline T &_ElementAt(size_t Index)
{
return this->_Content[Index];
}
inline const T &_ElementAt(size_t Index) const
{
return this->_Content[Index];
}
public:
Span() = default;
Span(size_t Size) : _Length(Size), _Content(new T[Size]) {}
Span(size_t Size, const T &Value) : _Length(Size), _Content(new T[Size])
{
for (size_t i = 0; i < _Length; i++)
{
_Content[i] = Value;
}
}
Span(Span &&Other) : _Length(Other._Length), _Content(Other._Content)
{
Other._Content = nullptr;
Other._Length = 0;
}
Span(const Span &Other) : _Length(Other._Length), _Content(new T[Other._Length])
{
for (size_t i = 0; i < Other._Length; i++)
{
_Content[i] = Other._Content[i];
}
}
Span(const T *Array, size_t Size) : _Length(Size), _Content(new T[Size])
{
for (size_t i = 0; i < Size; i++)
{
_Content[i] = Array[i];
}
}
Span(std::initializer_list<T> list) : _Length(list.size()), _Content(new T[list.size()])
{
size_t i = 0;
for (auto &item : list)
{
_Content[i] = item;
i++;
}
}
~Span()
{
delete[] _Content;
_Content = nullptr;
}
inline T *Content()
{
return _Content;
}
inline const T *Content() const
{
return _Content;
}
inline size_t Length() const
{
return _Length;
}
T &operator[](const size_t &Index)
{
if (Index >= _Length)
throw std::out_of_range("");
return _ElementAt(Index);
}
const T &operator[](const size_t &Index) const
{
if (Index >= _Length)
throw std::out_of_range("");
return _ElementAt(Index);
}
Span &operator=(const Span &Other)
{
if (this != &Other)
{
_Length = Other._Length;
delete[] _Content;
_Content = new T[_Length];
for (size_t i = 0; i < _Length; i++)
{
_Content[i] = Other._Content[i];
}
}
return *this;
}
Span &operator=(Span &&Other)
{
if (this != &Other)
{
delete[] _Content;
_Content = Other._Content;
_Length = Other._Length;
Other._Content = nullptr;
Other._Length = 0;
}
return *this;
}
};
template <typename T>
class BQueue final
{
public:
// Constructors
BQueue() = default;
BQueue(size_t Size, bool Growable = true) : _Content(Size), _First(0), _Length(0), _Growable(Growable) {}
BQueue(std::initializer_list<T> list) : _Content(list), _First(0), _Length(list.size()), _Growable(true) {}
BQueue(const BQueue &Other) : _Content(Other._Content), _First(Other._First), _Length(Other._Length), _Growable(Other._Growable) {}
BQueue(BQueue &&Other) : _Content(Other._Content), _First(Other._First), _Length(Other._Length), _Growable(Other._Growable)
{
Other._First = 0;
Other._Length = 0;
Other._Growable = true;
}
// Operators
BQueue &operator=(const BQueue &Other)
{
if (this != &Other)
{
_Content = Other._Content;
_First = Other._First;
_Length = Other._Length;
_Growable = Other._Growable;
}
return *this;
}
BQueue &operator=(BQueue &&Other)
{
if (this != &Other)
{
_Content = std::move(Other._Content);
_First = std::move(Other._First);
_Length = std::move(Other._Length);
_Growable = std::move(Other._Growable);
Other._First = 0;
Other._Length = 0;
Other.Growable = true;
}
return *this;
}
T &operator[](size_t Index)
{
if (Index >= _Length)
throw std::out_of_range("Index out of range");
return _Content.Content()[(_First + Index) % Capacity()];
}
T const &operator[](size_t Index) const
{
if (Index >= _Length)
throw std::out_of_range("Index out of range");
return _Content.Content()[(_First + Index) % Capacity()];
}
// Peroperties
size_t Capacity() const
{
return _Content.Length();
}
size_t Length() const
{
return _Length;
}
bool Growable() const
{
return _Growable;
}
T *Content()
{
return _Content.Content();
}
T const *Content() const
{
return _Content.Content();
}
inline bool IsWrapped() const
{
return _First + _Length > Capacity();
}
inline bool IsEmpty() noexcept { return _Length == 0; }
inline bool IsFull() noexcept { return _Length == Capacity(); }
inline size_t IsFree() noexcept { return Capacity() - _Length; }
// Helper functions
T &Head()
{
AssertNotEmpty();
return _Content.Content()[_First];
}
T const &Head() const
{
AssertNotEmpty();
return _Content.Content()[_First];
}
// Remove functionality
void Pop()
{
std::destroy_at(std::addressof(Head()));
--_Length;
_First = (_First + 1) % Capacity();
}
private:
Iterable::Span<T> _Content;
size_t _First = 0;
size_t _Length = 0;
bool _Growable = true;
inline void AssertNotEmpty()
{
if (IsEmpty())
throw std::out_of_range("Instance is empty");
}
};
}
}
class Messenger
{
public:
int Id = -1;
Messenger() = default;
Messenger(size_t id) : Id(id)
{
std::cout << Id << " Constructed" << std::endl;
}
Messenger(Messenger &&Other) : Id(Other.Id)
{
Other.Id = -1;
}
Messenger(Messenger const &Other) : Id(Other.Id) {}
Messenger &operator=(Messenger &&Other)
{
Id = Other.Id;
Other.Id = -1;
return *this;
}
Messenger &operator=(Messenger const &Other)
{
Id = Other.Id;
return *this;
}
~Messenger()
{
if (Id != -1)
{
std::cout << Id << " Destructed" << std::endl;
Id = -1;
}
}
};
using namespace Core;
int main(int argc, char const *argv[])
{
Iterable::BQueue<Messenger> Queue{1, 2};
Queue.Pop();
Queue.Pop();
std::cout << "Finished" << std::endl;
return 0;
}在这里,我得到的输出:
1 Constructed
2 Constructed
1 Destructed
2 Destructed
Finished
2 Destructed
1 Destructed正如你所看到的,在打印完成后,没有其他的东西必须被打印。
发布于 2022-08-28 13:08:11
好的,在linkedin上的Jason和的帮助下,我找到了问题所在。
问题是使用Span来保存数据!当Span超出作用域时,它将调用数组对象的所有析构函数,尽管并不是所有的数组对象都被构造并需要销毁,这将导致双释放。为了解决这个问题,我编写了一个只执行malloc(新的)和free(删除)的内存保持类,并且在BQueue的析构函数中,它只处理从_First到_First + _Length的对象,而不处理其他未构造的对象。
https://stackoverflow.com/questions/72577725
复制相似问题