Как предотвратить удаление объекта с помощью указателя на его родительский тип? - PullRequest
2 голосов
/ 09 мая 2020
struct A
{};

struct B : A
{
    operator A() const = delete; // not work
};

int main()
{
    B* p_derived = new B();
    delete p_derived; // ok

    // How to make the following two lines illegal?
    A* p_base = new B();
    delete p_base; 
}

A определение не может быть изменено, и его деструктор не virtual; поэтому объект B не должен удаляться указателем на A.

Я не могу использовать struct B : protected A {};, потому что хочу, чтобы B унаследовал все public члены A и оставьте их неподвижными public.

В шаблонах микширования C ++ это обычная проблема:

Это нормально, если класс миксина не удален полиморфно через указатель на исходный класс.

Мой вопрос:

Как предотвратить удаление объекта с помощью указателя на его родительский тип?

Ответы [ 2 ]

1 голос
/ 09 мая 2020

Короткий ответ: вы не можете.

Рассмотрим такую ​​функцию

// definition of struct A

void func(A *p)
{
    delete p;
}

и вызывающий объект в другом модуле компиляции

// definitions of struct A and B as in question

void func(A *);

void caller()
{
     func(new B);
}

In В этом случае код в func() вообще не имеет видимости типа B, не говоря уже о потенциале того, что указатель p, который он получает, фактически указывает на B. Поскольку func() делает delete p, функция caller() вызвала неопределенное поведение func().

Реальное решение в этом случае состоит в том, что B не должен наследовать от A . Уже есть явный намек на это в том факте, что A не имеет деструктора virtual. Это предотвратит caller() вызов func() с B *.

Если вы хотите, чтобы B предоставлял тот же интерфейс, что и A, тогда вам нужно использовать композицию и определить все функции-члены из B, поэтому они пересылаются в содержащийся A. Например;

 struct B
 {
      B() : a() {};    

      void some_member(some_type arg)
      {
          a.some_member(arg);      // assume A has this member that accepts these arguments
      }

      private:

         A a;
 };

Очевидно, что доступны варианты, если struct A можно изменить, но в вопросе явно указан вариант использования, когда это невозможно.

1 голос
/ 09 мая 2020

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

struct A
{
protected:
   ~A() {}
};

struct B :  A
{
};

int main()
{
    A* p_base = new B();
    delete p_base; // This is now an error
}

Если вы действительно не можете изменить A, вы можете сделать это:

struct A
{
};

struct AA : A
{
protected:
   ~AA() {}
};

struct B :  AA
{
};

int main()
{
    AA* p_base = new B();
    delete p_base; // How to make this a compile-time error?
}

Это не работает, если вы удаляете через A, а не через AA.

В зависимости от характера вашей проблемы вы можете сделать '#define A AA' или другой подобный уродливый прием, чтобы заставить эту работу работать с существующей кодовой базой.

struct A
{
};

struct AA : A
{
protected:
   ~AA() {}
};

#define A AA
struct B :  A
{
};

int main()
{
    A* p_base = new B();
    delete p_base; // How to make this a compile-time error?
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...