Как я могу удалить все указатели на один и тот же адрес? C

#c #memory #memory-management #smart-pointers

#c #память #управление памятью #интеллектуальные указатели

Вопрос:

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

Проблема заключается в следующем, когда мой интеллектуальный указатель начинает вызывать деструктор, это проверяет, нет ли моего указателя, nullptr тогда, если это правда, это приведет к удалению ptr.

После этого, когда деструктор снова вызывал приведения, я получаю исключение, потому что деструктор пытается удалить уже удаленный элемент, и мой оператор if во второй раз не работает (как я и ожидал), потому что после удаления элемента адрес меняется, а указатель не отображается.больше null.

Как я могу улучшить этот код и как я могу не удалять дважды уже удаленный указатель?

 #include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <memory>

using std::cout;
using std::endl;

template<typename T>
class Smart_Pointer
{
private:
    T* ptr;
public:
    Smart_Pointer(T* ptr);
    ~Smart_Pointer();
    Tamp; operator*();
};
template<typename T>
Smart_Pointer<T>::Smart_Pointer(T* ptr)
{
    this->ptr = ptr;
}

template<typename T>
Smart_Pointer<T>::~Smart_Pointer()
{
    if (ptr != nullptr)
    {
        delete ptr;
        ptr = nullptr;
        
    }

}

template<typename T>
Tamp; Smart_Pointer<T>::operator*()
{
    return *ptr;
}

int main()
{
    Smart_Pointer<int> castS(new int(10));
    Smart_Pointer<int> castS2 = castS;
}
  

Комментарии:

1. Это не то, как работают интеллектуальные указатели.

2. Это не решает вопрос, но delete ptr; word просто отлично, если ptr это нулевой указатель; в этом нет необходимости if (ptr != nullptr) . И установка ptr = nullptr; после удаления бессмысленна, потому что объект уничтожается и ptr больше не будет существовать.

3. Smart_Pointer(Smart_Pointer constamp;) = delete; потому что созданный компилятором конструктор копирования не будет работать. И Smart_Pointeramp; operator=(Smart_Pointer constamp;) = delete; для оператора присваивания, по тем же причинам.

Ответ №1:

Интеллектуальный указатель — это обобщающий термин. Для библиотеки std, которую он описывает unique_ptr , shared_ptr и weak_ptr .

Если вы хотите реализовать уникальный ptr, вам необходимо убедиться, что необработанный указатель принадлежит только одному уникальному ptr, поэтому вам нужно удалить конструктор копирования и оператор присваивания копирования. Чтобы иметь возможность передавать права собственности между вашими уникальными указателями, вы должны предоставить конструктор перемещения и оператор присваивания перемещения таким образом, чтобы право собственности передавалось.

Если вы хотите реализовать общий ptr, вам необходимо реализовать подсчет ссылок.

Как я могу удалить все указатели на один и тот же адрес? C

Это то, чего вы не хотите делать, вы хотите сохранить управляемый объект в рабочем состоянии до тех пор, пока существует хотя бы один общий указатель, владеющий этим управляемым объектом.

Правило трех / пяти / нуля:

Правило трех: если для класса требуется определяемый пользователем деструктор, определяемый пользователем конструктор копирования или определяемый пользователем оператор присваивания копирования, почти наверняка требуются все три.

Правило пяти: поскольку наличие определяемого пользователем деструктора, конструктора копирования или оператора присваивания копирования предотвращает неявное определение конструктора перемещения и оператора присваивания перемещения, любой класс, для которого желательна семантика перемещения, должен объявлять все пять специальных функций-членов:

Ответ №2:

Как я могу удалить все указатели на один и тот же адрес? C

Вероятно, вы не можете сделать это надежно и автоматически. Помните о теореме Райса и читайте больше о программировании на C , затем смотрите эту ссылку на C . Поймите, что указатели организуют ваше виртуальное адресное пространство в виде некоторого ориентированного графа, который развивается во время выполнения вашей программы.

Может быть, вы хотите очистить все указатели на один и тот же адрес.

Затем подробнее о сборке мусора.

Например, прочитайте руководство по GC .

Рассмотрите возможность использования какого-либо инструмента статического анализа в вашем исходном коде C , например, статического анализатора Clang.

Подумайте также о создании некоторого вашего кода на C (например, SWIG или GNU bison). Вы можете закодировать свой генератор кода C (например, используя GPP или GNU m4, или свой собственный генератор файлов C ), чтобы упростить управление вашими указателями.

Прочитайте также n3337 (некоторый проект стандарта C ) и документацию вашего компилятора C (возможно, GCC).

Имейте в виду, что подсчет ссылок имеет недостатки (например, он не поддерживает многопоточность).

Изучите для вдохновения исходный код существующих программ с открытым исходным кодом C (например, на github), таких как Fish, Qt, RefPerSys, GCC, Clang, ANTLR. Рассмотрите возможность внесения вклада в один из них.

Ответ №3:

Если вы пытаетесь эмулировать поведение unique_ptr , это решается простым запретом копирования. Если вы удалите конструктор копирования, никакие два интеллектуальных указателя не могут указывать на один и тот же адрес одновременно. Вы просто пишете:

 SmartPtr(const SmartPtramp;) = delete;
  

Однако, если вы сделаете это, вам может понадобиться способ передачи права собственности, поэтому было бы неплохо реализовать конструктор перемещения:

 SmartPtr(SmartPtramp;amp; other) {
    ptr = std::exchange(other.ptr, nullptr);
}
  

Или что-то в этом роде. Если вы действительно хотите, чтобы два интеллектуальных указателя указывали на один и тот же адрес, вам нужен способ решить, какой из них будет удалять ptr, обычно последний, который выходит из области видимости. Способ, которым это делается в стандарте (например. В shared_ptr) заключается в определении общей структуры между всеми экземплярами класса, но это, вероятно, выходит за рамки этого ответа..

Комментарии:

1. Я исправил это. Однако это не так, просто отличается от стандартного

2. Что вы подразумеваете под «отличным от стандартного»? Это UB, потому что вы читаете неинициализированную переменную и delete должны произойти сбои, если указатель не окажется равным нулю.

3. Я вижу, вы правы. Я думал переместить назначение.

Ответ №4:

Как я могу удалить все указатели на один и тот же адрес?

Первая проблема заключается в том, что вы не знаете, сколько точек на один и тот же адрес.

Это можно решить многими способами.

  1. Используя блок управления (например, std::shared_ptr), все интеллектуальные указатели указывают на блок управления, который указывает на реальный объект и имеет счетчик.
  2. Используя навязчивый счетчик в указателе на объект, либо наследуйте его, либо объект должен быть унаследован счетчиком.
  3. Использование внешнего свободного счетчика.
  4. Проанализируйте всю память, чтобы увидеть, указывает ли что-нибудь на этот адрес (сборка мусора, GC).

Счетчик должен быть атомарным, если вы намерены выполнять многопоточность.

Реализация внешнего свободного счетчика имеет огромное преимущество в том, что у вас нет косвенности 1. и у вас нет навязчивости 2 или сложности GC.

Непроверенный код

 std::unordered_map<void *, std::atomic_int> extro_count;
  

Затем вам нужно реализовать правило 3 или правило 5 в зависимости от того, насколько продвинутым вы хотите его сделать.

 template<typename T>
Smart_Pointer<T>::~Smart_Pointer() {
  if (ptr != nullptr) {
    if (--extro_count[ptr] == 0) {
      extro_count.erase(ptr);
      delete ptr;
      ptr = nullptr;
  }
}
  

Я оставляю реализацию остальных в OP.

Это все еще очень небезопасно в потоковом коде, если вы не будете осторожны.