управление памятью — C ++ помещает несколько типов в вектор

Замечания: Я знаю, что подобные вопросы были заданы на SO раньше, но я не нашел их полезными или очень ясными.

Второе примечание: В рамках данного проекта / задания я стараюсь избегать сторонних библиотек, таких как Boost.

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

vector<something magical to hold various types> vec;
int x = 3;
string hi = "Hello World";
MyStruct s = {3, "Hi", 4.01};

vec.push_back(x);
vec.push_back(hi);
vec.push_back(s);

я слышал vector<void*> может работать, но тогда это становится сложным с распределением памяти, и тогда всегда есть вероятность, что определенные части в соседней памяти могут быть непреднамеренно переопределены, если значение, вставленное в определенный индекс, больше, чем ожидалось.

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

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

Спасибо за ваше время.

15

Решение

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

Вероятно, также хорошо, чтобы этот класс генерировал исключение, когда вы пытаетесь вернуть Type-A, когда вы ранее сохранили в нем Type-B.

Вот часть класса Holder из одного из моих проектов. Вы, вероятно, можете начать отсюда.

Примечание: из-за использования неограниченных союзов это работает только в C ++ 11. Более подробную информацию об этом можно найти здесь: Какие неограниченные союзы предлагаются в C ++ 11?

class Holder {
public:
enum Type {
BOOL,
INT,
STRING,
// Other types you want to store into vector.
};

template<typename T>
Holder (Type type, T val);

~Holder () {
// You want to properly destroy
// union members below that have non-trivial constructors
}

operator bool () const {
if (type_ != BOOL) {
throw SomeException();
}
return impl_.bool_;
}
// Do the same for other operators
// Or maybe use templates?

private:
union Impl {
bool   bool_;
int    int_;
string string_;

Impl() { new(&string_) string; }
} impl_;

Type type_;

// Other stuff.
};
7

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

Объекты, удерживаемые std::vector<T> должны быть однородного типа. Если вам нужно поместить объекты разного типа в один вектор, вам нужно как-то стереть их тип и сделать их все похожими. Вы можете использовать моральный эквивалент boost::any или же boost::variant<...>, Идея boost::any заключается в том, чтобы инкапсулировать иерархию типов, сохраняя указатель на базу, но указывая на шаблонизированный производный. Очень грубый и неполный контур выглядит примерно так:

#include <algorithm>
#include <iostream>

class any
{
private:
struct base {
virtual ~base() {}
virtual base* clone() const = 0;
};
template <typename T>
struct data: base {
data(T const& value): value_(value) {}
base* clone() const { return new data<T>(*this); }
T value_;
};
base* ptr_;
public:
template <typename T> any(T const& value): ptr_(new data<T>(value)) {}
any(any const& other): ptr_(other.ptr_->clone()) {}
any& operator= (any const& other) {
any(other).swap(*this);
return *this;
}
~any() { delete this->ptr_; }
void swap(any& other) { std::swap(this->ptr_, other.ptr_); }

template <typename T>
T& get() {
return dynamic_cast<data<T>&>(*this->ptr_).value_;
}
};

int main()
{
any a0(17);
any a1(3.14);
try { a0.get<double>(); } catch (...) {}
a0 = a1;
std::cout << a0.get<double>() << "\n";
}
17

Как и предполагалось, вы можете использовать различные формы союзов, вариантов и т. Д. В зависимости от того, что вы хотите сделать с вашими хранимыми объектами, внешний полиморфизм может делать именно то, что вы хотите если вы можете определить все необходимые операции в интерфейсе базового класса.

Вот пример, если все, что мы хотим сделать, это вывести объекты на консоль:

#include <iostream>
#include <string>
#include <vector>
#include <memory>

class any_type
{
public:
virtual ~any_type() {}
virtual void print() = 0;
};

template <class T>
class concrete_type : public any_type
{
public:
concrete_type(const T& value) : value_(value)
{}

virtual void print()
{
std::cout << value_ << '\n';
}
private:
T value_;
};

int main()
{
std::vector<std::unique_ptr<any_type>> v(2);

v[0].reset(new concrete_type<int>(99));
v[1].reset(new concrete_type<std::string>("Bottles of Beer"));

for(size_t x = 0; x < 2; ++x)
{
v[x]->print();
}

return 0;
}
7
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector