簡體   English   中英

C++ Queue::Pop function 在使用 -O1 編譯時行為怪異

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM