Ошибка компиляции Реализация пользовательского общего указателя с предварительно выделенной памятью [на удержании]

Я читал о том, как работают общие указатели. Я использую std :: shared_ptr и std :: unique_ptr в моем проекте, и они работают хорошо. Однако в некоторых случаях мне требуется (во время компиляции) предварительно выделенная память.

В Codeproject есть хорошее руководство по реализации общих указателей. Я в значительной степени следовал этому уроку. Я бы хотел, чтобы вы просмотрели мой код и сказали, что я что-то пропустил, потому что ошибки в памяти трудно отследить.

Содержимое моего общего указателя

1. Интерфейс предварительно выделенной памяти

Интерфейс для запроса предварительно выделенного буфера записан на C. Детали этой реализации не имеют значения, но суммируются:


#define TRANSMISSION_BUFFER_LENGTH 200u ///< Data Array Length


typedef struct
{
        // some other stuff....
    uint8_t data[TRANSMISSION_BUFFER_LENGTH];  ///< Data
} Transmission_Buffer_T;


/**
 * Requests a new preallocated buffer
 * @return Pointer to the buffer of NULL if no buffer available
 */
Transmission_Buffer_T* Get_New_Transmission_Buffer();

///Free Buffer
void Free_Transmission_Buffer(Transmission_Buffer_T* pBuffer);

2. Счетчик ссылок

Как и все общие указатели, моя реализация также использует подсчет ссылок. Там нет магии. Но только для полноты:

class CReference_Count
{
    public:

        ///Increments the Counter by 1
        void Increment();

        /**
         * Decrements the Counter by 1 an returns the value
         * @return Count after decrement
         */
        int Decrement();


    private:
        ///The actual reference counter
        std::atomic count = 0;
};


void CReference_Count::Increment()
{
    ++count;
}

int CReference_Count::Decrement()
{
    --count;
    return count;
}


3. Класс Smart Pointer

Теперь фактический класс Smart Pointer содержит 3 свойства

  1. Указатель на данные как универсальный тип T * (это фактически указывает на pTransmission_Buffer-> data)
  2. Указатель на предварительно выделенный буфер
  3. Объект подсчета ссылок хранится в куче.

Некоторые из вас могут удивиться, почему я храню (1) и (2) отдельно, хотя указывают на одни и те же данные. Я не мог найти лучшее решение. Мне нужен указатель pTransmission_Buffer для фактического освобождения памяти и T * для правильного доступа к памяти.

Реализация содержит конструкторы 1. Конструктор Nullptr 2. Конструктор «по умолчанию» 3. Конструктор копирования 4. Конструктор перемещения

(нет, я знаю, что мне не хватает конструктора псевдонимов, однако я не уверен, что эта вещь на самом деле делает, и если мне это нужно)

при условии: операторы 1. Оператор указателя * 2. Оператор указателя -> 3. Оператор назначения = (Копировать) 4. Оператор назначения = (назначение перемещения)

template
class CTransmission_Based_Shared_Ptr
{
private:

    /// Pointer to the actual data
    /// @remarks (Pointer to a preallocated transmission buffer)
    /// Points to pTransmission_Buffer->data
    T* pValue;

    ///Pointer to the actual preallocated memory buffer
    Transmission_Buffer_T* pTransmission_Buffer;

    /// Reference Count
    /// @remarks Stored on the heap
    CReference_Count* pReference;

    /**
     * Cleans all Ressources used in the given class instance
     * @param class_instance class instance to clean
     */
    static void Cleanup(CTransmission_Based_Shared_Ptr& class_instance)
    {
        //Delete Referencecount Object
        delete class_instance.pReference;
        class_instance.pReference = nullptr;

        if (nullptr != class_instance.pValue)
        {
            // destroy the existing resource.
            class_instance.pValue->~T();
            class_instance.pValue = nullptr;
        }

        if (nullptr != class_instance.pTransmission_Buffer)
        {
            //free preallocated memory
            Free_Transmission_Buffer(class_instance.pTransmission_Buffer);
            class_instance.pTransmission_Buffer = nullptr;
        }

    }

public:
    ///Nullpointer Constructor
    CTransmission_Based_Shared_Ptr() :
                    pValue(nullptr),
                    pTransmission_Buffer(nullptr),
                    pReference(nullptr)
    {
        // Create a new reference
        pReference = new CReference_Count();
        // Increment the reference count
        pReference->Increment();
    }

    ///"default" constructor
    CTransmission_Based_Shared_Ptr(Transmission_Buffer_T* pBuffer) :
                    pValue(reinterpret_cast(pBuffer->data)),
                    pTransmission_Buffer(pBuffer),
                    pReference(nullptr)
    {
        static_assert(sizeof(T) <= TRANSMISSION_BUFFER_LENGTH, "Buffer length exceeded");

        // Create a new reference
        pReference = new CReference_Count();
        // Increment the reference count
        pReference->Increment();
    }

    ///Copy Constructor
    CTransmission_Based_Shared_Ptr(const CTransmission_Based_Shared_Ptr& sp) :
                    pValue(sp.pValue),
                    pTransmission_Buffer(sp.pTransmission_Buffer),
                    pReference(sp.pReference)
    {
        // Copy constructor
        // Copy the data and reference pointer
        // and increment the reference count
        pReference->Increment();
    }

    ///Move Constructor
    CTransmission_Based_Shared_Ptr(CTransmission_Based_Shared_Ptr&& other) :
                    pValue(std::move(other.pTransmission_Buffer)),
                    pTransmission_Buffer(std::move(other.pTransmission_Buffer)),
                    pReference(std::move(other.pReference))
    {
        /*
         * TODO Question: Why does this fail for the last part
         * pReference(std::move(other.pReference))
         *
         * Error:
         * cannot convert 'std::remove_reference::type {aka Transmission_Buffer_T*}' to
         *  'NTestcases::Smart_Pointer_Dummy_Struct_T*' in initialization
         */

        other.pReference = nullptr;
        other.pTransmission_Buffer = nullptr;
        other.pValue = nullptr;
    }

    ///Destructor
    ~CTransmission_Based_Shared_Ptr()
    {
        // Destructor
        // Decrement the reference count
        // if reference become zero delete the data
        if (0 == pReference->Decrement())
        {
            Cleanup(*this);
        }
    }

    T& operator*()
    {
        if (nullptr == pValue)
        {
            NException::CException ex("Nullpointer", __FILE__, __LINE__);
            throw ex;
        }
        return *pValue;
    }

    T* operator->()
    {
        if (nullptr == pValue)
        {
            NException::CException ex("Nullpointer", __FILE__, __LINE__);
            throw ex;
        }
        return pValue;
    }

    const T& operator*() const
    {
        if (nullptr == pValue)
        {
            NException::CException ex("Nullpointer", __FILE__, __LINE__);
            throw ex;
        }
        return *pValue;
    }

    const T* operator->() const
    {
        if (nullptr == pValue)
        {
            NException::CException ex("Nullpointer", __FILE__, __LINE__);
            throw ex;
        }
        return pValue;
    }

    /// Copy operator
    CTransmission_Based_Shared_Ptr& operator =(const CTransmission_Based_Shared_Ptr& sp)
    {
        // Assignment operator
        if (this != &sp) // Avoid self assignment
        {
            // Decrement the old reference count
            // if reference become zero delete the old data
            if (0 == pReference->Decrement())
            {
                Cleanup(*this);
            }

            // Copy the data and reference pointer
            // and increment the reference count
            pValue = sp.pValue;
            pReference = sp.pReference;
            pReference->Increment();
        }
        return *this;
    }

    /// Move assignment operator.
    CTransmission_Based_Shared_Ptr& operator=(CTransmission_Based_Shared_Ptr && other)
    {

        if (this != &other)
        {
            //delete possible old data
            Cleanup(*this);

            //swap the data
            std::swap(this->pReference, other.pReference);
            std::swap(this->pTransmission_Buffer, other.pTransmission_Buffer);
            std::swap(this->pValue, other.pValue);
        }
        return *this;
    }

};

4. Функция Make__xx ()

И последнее, но не менее важное: у каждого умного указателя есть своя собственная функция make (). Так что я также предоставил один. Так как я использую предварительно выделенную память, я должен использовать новое размещение.

template
CTransmission_Based_Shared_Ptr make_transmission_based_shared_pointer(Arg&& ... args)
{
    static_assert(sizeof(Value_T) <= TRANSMISSION_BUFFER_LENGTH, "Buffer length exceeded");

    Transmission_Buffer_T* pBuffer = Get_New_Transmission_Buffer();
    if (nullptr == pBuffer)
    {
        NException::CException ex("No free buffer", __FILE__, __LINE__);
        throw ex;
    }

    // Placement new
    //allocate memory inside transmission buffer
    volatile Value_T* value = new (pBuffer->data) Value_T { std::forward(args)... };
    UNUSED(value);

    auto res = CTransmission_Based_Shared_Ptr { std::move(pBuffer) };

    return res;
}

Вопросы:

1) Заметили ли вы ошибки в моей реализации?

2) Не могли бы вы сказать мне, почему я получаю ошибка в моем конструкторе перемещения.

(ошибка: не удается преобразовать 'std :: remove_reference :: type {aka Transmission_Buffer_T }' в 'NTestcases :: Smart_Pointer_Dummy_Struct_T *' при инициализации) *

Эти 2 строки вызвали ошибка компилятора

auto res = make_transmission_based_shared_pointer();

и последняя часть списка инициализаторов в конструкторе перемещения

    CTransmission_Based_Shared_Ptr(CTransmission_Based_Shared_Ptr&& other) :
                    pValue(std::move(other.pTransmission_Buffer)),
                    pTransmission_Buffer(std::move(other.pTransmission_Buffer)),
                    // vvvvvvvv This line causes the Error
                    pReference(std::move(other.pReference))

Не могли бы вы помочь мне выяснить ошибку компилятора?

Изменить: изменил заголовок заголовка и добавил раздел вопросов в конце

Редактировать 2: Через некоторое время, глядя на свой собственный код, я обнаружил ошибку в конструкторе перемещения

 ///Move Constructor
    CTransmission_Based_Shared_Ptr(CTransmission_Based_Shared_Ptr&& other) :
                    pValue(std::move(other.pTransmission_Buffer)), //< This has to be other.pValue
                    pTransmission_Buffer(std::move(other.pTransmission_Buffer)),
                    pReference(std::move(other.pReference))

После того, как я изменил это, он работал нормально. Я не уверен, как пометить этот ответ как решение, поэтому я отредактировал свой оригинальный пост

0
задан Dr.Death 27 June 2019 в 20:15
поделиться