Подтвердить что ты не робот

Запретить удаление указателя в С++

Есть ли способ предотвратить удаление указателя на С++ путем его объявления?

Я пробовал следующий код без везения.

const int* const foo()
{
    static int a;
    return &a;
}

int main()
{
    const int* const a = foo();

    *a = 1;   //compiler error, const int*
    a++;      //compiler error, int* const
    delete a; //no compiler error, I want to have compiler error here

    return 0;
}
4b9b3361

Ответ 1

Вы не можете объявить указатель на произвольный тип способом, который предотвращает вызов delete в указателе. Удаление указателя на const (T const *) объясняет, почему это так.

Если это был указатель на пользовательский класс, вы могли бы сделать оператор delete закрытым:

class C {
    void operator delete( void * ) {}
};

int main() {
    C *c;
    delete c; // Compile error here - C::operator delete is private!
}

Вы, конечно же, не должны делать деструктор закрытым (как это было предложено другими), поскольку он также избегал бы создавать объекты в стеке:

class C {
    ~C() {}
};

int main() {
    C c; // Compile error here - C::~C is private!
}

Ответ 2

Простой ответ - нет. Невозможно исключить удаление из указателя на встроенный тип.

ДОПОЛНЕНИЕ:

Однако я столкнулся с подобными ситуациями. Мое решение заключалось в том, чтобы прекратить использовать обычный указатель и, следовательно, не нужно беспокоиться об удалении. В моем случае общий указатель имел смысл, но вам может быть достаточно уникального указателя или подобного.

//Custom do nothing deleter.
template<typename T> dont_delete( T* ) { /* Do Nothing */ }

shared_ptr<const int> const foo()
{
  static int a;
  return shared_ptr<const int>(&a, &dont_delete<const int> );
}

shared_ptr<const int> const bar()
{
  return shared_ptr<const int>(new int(7) );
}

main()
{
   shared_ptr<const int> p1 = foo();
   shared_ptr<const int> p2 = bar();

   //p1s data _not_ deleted here, 
   //p2s data is deleted here
}

Ответ 3

Я не совсем понимаю, о чем вы спрашиваете. Если вам нужен объект, который нельзя удалить, вы можете попробовать сделать foo классом и сделать деструктор закрытым.

class Foo {
public:
   int a;

   Foo(int v) {
       a = b;
   }

private:
   ~Foo() { }
};

int main() {

    Foo *c = new Foo(1);

    delete c; // compiler error, ~Foo() is private

    return 0;
}

Я сделал переменную "a" публичной, так как она была первоначально определена как структура, но вы можете (и должны) сделать ее конфиденциальной и сделать аксессуар, который обеспечивает соблюдение правил доступа, которые вам нужны в вашем примере исходного кода.

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

Ответ 4

Я думаю, что он означает случайное удаление объекта (будь то удаление o или free (o)), что может привести к сбою программы. Нет никакого способа обойти это с объектом, выделенным на кучу; поскольку на указателях стека все мы знаем, что этого не может быть. Использовать защищенный dtor в классах верхнего уровня - это вариант, но тогда вам нужно вызвать его в дочернем классе dtor.

Одно решение (даже если переопределить оператор удаления в таблице) заключается в использовании системы сопоставления таблиц, которая возвращает id/token/what-have-you, но это действительно работает только на вашем языке, пишут в коде CSTYLE и компилируют в соглашениях C. Про делать это скрыто, указатели объектов от пользователя позволяют пользователю передавать в токен, который отображается на объект. Это требует работы и опыта.

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

Ответ 5

Вы можете запретить использование оператора delete при указателе определенных классов. Например:

class Object {
public: void operator delete(void* p) = delete;
};

class Entity : public Object {    };

int main(int argc, char const *argv[])
{
    Object* o = new Object;
    Entity* e = new Entity;

    delete o; // compiler error
    delete e; // compiler error

    return 0;
}

Для всех классов, которые наследуются от Object, нельзя удалить, поскольку удаление объекта Object:: delete было удалено. Не помещайте этот оператор как закрытый, поскольку он дает ошибку компилятора при получении или создании экземпляра класса Object. Имейте в виду, что мы все еще можем это сделать:

::operator delete(o);

который освободит указатель o, но не вызовет деструктор. Используйте класс для управления временем жизни класса Object. Простая реализация:

class Object {
    template<typename type> friend class ptr;
    public: void operator delete(void* p) = delete;
};

class Entity : public Object { };

template<typename type>
class Ptr {
public:
    Ptr(type* obj) : o(obj){}
    ~Ptr() { o->~type(); ::operator delete(o); }
private: type* o;
};

int main(int argc, char const *argv[])
{
    Object* o = new Object;
    Entity* e = new Entity;

    // delete o;  // error
    // delete e;  // error

    Ptr<Entity> ent = new Entity; // Ptr will delete ent for you.

    return 0;
}