[英]C++ Queue::Pop function acts weird when compiled with -O1
我一直在 c++ 中編寫自己的隊列。 問題是它有一個名為 Queue::Pop() 的 function,它調用隊列中第一項的析構函數,由名為 _First 的索引跟蹤,然后 _First 遞增,_Length 遞減。 當正常編譯時,它按預期執行,但是當添加 -O1 標志時,它開始表現得很奇怪並且沒有正確調用對象的析構函數。 析構函數中的打印發生但 Id 未設置為 -1。 結果,當隊列超出 scope 時, memory 被 delete[] 編輯並再次調用析構函數,這是無效代碼,因為如果它是文件描述符,它將被關閉兩次。 這是流行音樂 function:
#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;
}
這里是我得到的 output:
1 Constructed
2 Constructed
1 Destructed
2 Destructed
Finished
2 Destructed
1 Destructed
如您所見,打印完成后,無需打印任何其他內容。
好的,在 Linkin 上的 Jason Turner 和 Bjarne Stroustrup 的幫助下,我找到了問題所在。
問題是使用 Span 來保存數據。 當 Span 超出 scope 時,它將調用數組對象的所有析構函數,盡管事實上並非所有對象都被構造並且需要被析構,這將導致雙重釋放。 為了解決這個問題,我寫了一個 Memory 持有人 class ,它只處理 malloc(new) 和 free(delete) 並且在 BQueue 的析構函數中它只處理從 _First 到 _First + _Length 的對象,而不是其他未構造的對象。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.