контейнеры — C ++ Добавить производный объект абстрактного класса в другой класс без висячих указателей?

У меня есть простой контейнерный класс, который указывает на абстрактный класс, и у меня есть функции для получения / установки указателя в контейнерном классе. Конкретнее, класс выглядит так:

class Container
{
Abstract* thing;
public:
void set(Abstract &obj)
{
thing = &obj; //danger of dangling pointer
}

Abstract* get()
{
return thing;
}
};

Abstract это абстрактный класс. Как уже видно, существует опасность висящего указателя. Я знаю, что могу сделать копию объекта (нового) и затем указать на него. Но я не могу создать экземпляр абстрактного класса. Какие решения для этого есть?


Следующее — просто дополнительная информация:

Определения классов

class Abstract
{
public:
virtual void something() = 0;
};

class Base : public Abstract
{
int a;
public:
Base() {}
Base(int a) : a(a){}
virtual void something()
{
cout << "Base" << endl;
}
};

class Derived : public Base
{
int b;
public:
Derived() {}
Derived(int a, int b) : Base(a), b(b){}
virtual void something()
{
cout << "Derived" << endl;
}
};

Простые тесты

void setBase(Container &toSet)
{
Base base(15);
toSet.set(base);
}

void setDerived(Container &toSet)
{
Derived derived(10, 30);
toSet.set(derived);
}

int main()
{
Container co;

Base base(15);
Derived derived(10, 30);

Base *basePtr;
Derived *derivedPtr;

//This is fine
co.set(base);
basePtr = static_cast<Base *>(co.get());
basePtr->something();

//This is fine
co.set(derived);
derivedPtr = static_cast<Derived *>(co.get());
derivedPtr->something();

//Reset
basePtr = nullptr;
derivedPtr = nullptr;

//Dangling pointer!
setBase(co);
basePtr = static_cast<Base *>(co.get());
basePtr->something();

//Dangling pointer!
setDerived(co);
derivedPtr = static_cast<Derived *>(co.get());
derivedPtr->something();

return 0;
}

0

Решение

То, что вам нужно сделать, это определить ваше владение памятью конкретно.

Container::set принимает экземпляр Abstract по ссылке, которая обычно не подразумевает передачу права собственности:

 void set(Abstract &obj){...} // Caller retains ownership of obj, but now we have a weak reference to it

Тогда ответственность за удаление не на вас.

Container::get возвращает указатель что подразумевает владение, указывая, что кто-то, кто звонит set не должен лишать законной силы переданный объект.

Abstract* get(){...}

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

У вас есть несколько вариантов

  • Кодировать эту семантику владения памятью в Container с соответствующей документацией (Код по контракту)
  • Используйте умный указатель как std::shared_ptr

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

 void set(std::shared_ptr<Abstract> obj){...}
// now Container participates in the lifetime of obj,
// and it's harder to nullify the underlying object
// (you'd have to be intentionally misbehaving)
6

Другие решения

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

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

Некоторая информация может быть найдена здесь:
умные указатели (повышение) объяснил

1

Это то, что std::unique_ptr для:

class Container
{
std::unique_ptr<Abstract> thing;
public:
void set(std::unique_ptr<Abstract> obj)
{
thing = obj;
}

Abstract* get()
{
return thing.get();
}
};

Теперь Abstract объект «принадлежит» Container и будет очищен автоматически, когда Conatiner уничтожен

Если вы хотите указатель, который может жить дольше или может использоваться несколькими контейнерами, используйте std::shared_ptr вместо.

1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector