Запретить удаление указателя в С++
Есть ли способ предотвратить удаление указателя на С++ путем его объявления?
Я пробовал следующий код без везения.
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;
}
Ответы
Ответ 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;
}