WedX - журнал о программировании и компьютерных науках

Как сделать контейнер, который содержит указатели любого типа

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

vector<void*> v;

v.push_back(new Dog());
v.push_back(new Cat());

cout <<  v[0]; // prints mem address
cout <<  v[1]; // prints another mem address
cout << *v[0]; // compiler yells at me

Но, по-видимому, вы не можете разыменовывать пустые указатели. Есть ли способ создать общий контейнер указателей любого типа, не заставляя каждый отдельный класс расширять суперкласс под названием «Объект» или что-то в этом роде?

05.07.2014

  • Нет, вы не можете разыменовывать пустые указатели. 05.07.2014
  • Вы можете переинтерпретировать указатели void обратно к их реальному типу, а затем разыменовать их. Я понятия не имею, как вы могли бы определить реальный тип указателей, хотя:) 05.07.2014
  • Если вы хотите удалить их правильно, вам нужен интеллектуальный указатель или какая-либо другая форма стирания типа. 05.07.2014
  • "so that I can store every single pointer in there and then easily delete them later." Вы делаете это неправильно. Храните экземпляры, а не указатели на экземпляры. 05.07.2014
  • Кроме того, вы можете поделиться тем, что вы пытаетесь сделать (то есть более широкой картиной), потому что я понятия не имею, чего вы ожидаете, пытаясь разыменовать указатель void*, поэтому я не могу правильно объяснить, почему вы можете не делай этого по-твоему. 05.07.2014

Ответы:


1

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

class pointer_wrapper_base
{
public:
  virtual void delete_pointee()=0;
protected:
  void *m_ptr;
};

template<class T>
class pointer_wrapper: public pointer_wrapper_base
{
public:
  pointer_wrapper(T *ptr_) {m_ptr=ptr_;}
  virtual void delete_pointee()
  {
    delete (T*)m_ptr;
  }
};

Если у вас есть этот класс, вы можете использовать, например, поливариантный класс, который похож на класс вариантов, но все различные варианты имеют общий базовый класс. У меня есть реализация, если хотите посмотреть: https://sourceforge.net/p/spinxengine/code/HEAD/tree/sxp_src/core/utils.h (ищите poly_pod_variant):

  std::vector<poly_pod_variant<pointer_wrapper_base> > x;
  x.push_back(pointer_wrapper<Cat>(new(Cat)));
  x[0]->delete_pointee();

Или, если у вас все в порядке с динамическим распределением для оберток, вы, конечно, можете просто хранить указатели на pointer_wrap_base в векторе, например.

std::vector<std::unique_ptr<pointer_wrapper_base> > x;
x.push_back(std::unique_ptr<pointer_wrapper_base>(new(pointer_wrapper<Cat>)(new Cat)));
x[0]->delete_pointee();
05.07.2014

2

Попробуйте использовать некоторые классы Boost, такие как boost::any, https://www.boost.org/doc/libs/1_55_0/doc/html/any.html и их пример кода, https://www.boost.org/doc/libs/1_55_0/doc/html/any/s02.html

В качестве альтернативы, посмотрите на вариант Boost.

В общем, изучайте Boost. Это поразит вас и ускорит вашу разработку на C++.

05.07.2014

3

C++ имеет статическую систему типов. Это означает, что типы всех выражений должны быть известны во время компиляции. Решение вашей проблемы зависит от того, что вы собираетесь делать с объектами.

Вариант 1: пусть Cat и Dog будут производными от класса

Это имеет смысл, если все объекты имеют общий интерфейс и если вы можете сделать их производными от класса.

std::vector<std::unique_ptr<Animal>> vec; // good practice - automatically manage 
                                          // dynamically allocated elements with
                                          // std::unique_ptr

vec.push_back(std::make_unique<Dog>()); // or vec.emplace_back(new Dog());
vec.push_back(std::make_unique<Cat>()); // or vec.emplace_back(new Cat());

std::cout << *v[0];

Вариант 2: boost::any

Это имеет смысл, если типы не связаны. Например, вы храните int и объекты вашего класса. Очевидно, вы не можете сделать int производным от вашего класса. Таким образом, вы используете boost::any для сохранения, а затем возвращаете его к типу объекта. Исключение типа boost::bad_any_cast выдается, если вы приводите к несвязанному типу.

std::vector<boost::any> vec;
vec.push_back(Dog());
vec.push_back(25);

std::cout << boost::any_cast<int>(vec[1]);

А также указатели. Решение «Я хочу правильно управлять своей памятью»: «Не используйте new и delete». Вот инструменты, которые помогут вам в этом, в произвольном порядке:

  • std::string вместо строк с завершающим нулем
  • std::vector<T> вместо new T[]
  • std::unique_ptr<T> вместо необработанных указателей на полиморфные объекты
  • ...или std::shared_ptr<T>, если вы поделитесь ими
05.07.2014

4

cout ‹‹ * ((Собака *)v[0]);

Я предполагаю, что ваш класс Dog является строковым, иначе вы получите ошибку другого типа, но ваша проблема преобразования типа должна быть решена путем приведения типа (Dog *).

05.07.2014
Новые материалы

Объяснение документов 02: BERT
BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

Как проанализировать работу вашего классификатора?
Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

Работа с цепями Маркова, часть 4 (Машинное обучение)
Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

Учебные заметки: создание моего первого пакета Node.js
Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


Для любых предложений по сайту: [email protected]