Я пытаюсь создать вектор на С++, который может хранить 3 разных типа данных. Я не хочу использовать библиотеку boost. Что-то типа:
vector<type1, type2, type3> vectorName;
Нужно ли делать шаблон? И если да, то как бы я это сделал?
Я пытаюсь создать вектор на С++, который может хранить 3 разных типа данных. Я не хочу использовать библиотеку boost. Что-то типа:
vector<type1, type2, type3> vectorName;
Нужно ли делать шаблон? И если да, то как бы я это сделал?
РЕДАКТИРОВАТЬ: начиная с С++ 17, стандартная библиотека теперь включает шаблон класса std:: вариант, который очень похож на ранее существовавшие решения в boost. variant
— это типобезопасная альтернатива объединениям, которая позволяет объединять несколько типов с помощью отношения или, например, std::variant<type1, type2, typ3>
содержит либо type1
, либо type2
, либо type3.
. Он может быть составлен с std::vector
, чтобы получить именно то, что вы описали:
std::vector<std::variant<type1, type2, type3>> vectorName;
Однако std::variant
вводит некоторые ограничения. Например, он не может содержать ссылочные или массивные типы, а доступ к базовому типу (например, type1
или type2
) возможен только с помощью кода шаблона. Если std::variant
не обеспечивает нужного вам поведения, продолжайте читать, чтобы узнать о более сложном, но более универсальном подходе, который также имеет преимущество работы в любой версии C++.
ОРИГИНАЛЬНЫЙ ОТВЕТ:
Самый простой способ хранить несколько типов в одном векторе — сделать их подтипами родительского класса, обернув желаемые типы в классы, если они уже не являются классами.
class Parent {
// Anything common to the types should be declared here, for instance:
void print() { // Make this virtual if you want subclasses to override it
std::cout << "Printing!";
}
virtual ~Parent(); //virtual destructor to ensure our subclasses are correctly deallocated
};
class Type1 : public Parent {
void type1method();
};
class Type2 : public Parent {
void type2Method();
};
class Type3 : public Parent {
void type3Method();
};
Затем вы можете создать вектор из Parent
указателей, которые могут хранить указатели на дочерние типы:
std::vector<Parent*> vec;
vec.push_back(new Type1);
vec.push_back(new Type2);
vec.push_back(new Type3);
При доступе к элементам непосредственно из вектора вы сможете использовать только элементы, принадлежащие Parent
. Например, вы можете написать:
vec[0]->print();
Но нет:
vec[0]->type1Method();
Поскольку тип элемента был объявлен как Parent*
, а тип Parent
не имеет члена с именем type1Method
.
Если вам нужно получить доступ к членам, специфичным для подтипа, вы можете преобразовать указатели Parent
в указатели подтипа следующим образом:
Parent *p = vec[0];
Type1 *t1 = nullptr;
Type2 *t2 = nullptr;
Type3 *t3 = nullptr;
if (t1 = dynamic_cast<Type1*>(p)) {
t1->type1Method();
}
else if (t2 = dynamic_cast<Type2*>(p)) {
t2->type2Method();
}
else if (t3 = dynamic_cast<Type3*>(p)) {
t3->type3Method();
}
Хотя обычно считается, что лучше избегать такого явного ветвления типов и вместо этого полагаться на виртуальные методы.
Обязательно удалите указатели, прежде чем удалять их из вектора, если вы используете динамическое размещение, как я сделал в примере выше. В качестве альтернативы используйте интеллектуальные указатели (вероятно, std::unique_ptr
) и позвольте вашей памяти позаботиться о себе:
std::vector<std::unique_ptr<Parent>> vec;
Я пытаюсь создать вектор на С++, который может хранить 3 разных типа данных.
Ответ здесь действительно зависит от конкретного варианта использования:
Если объекты каким-то образом связаны и чем-то похожи — создайте базовый класс и выведите из него все классы, затем сделайте векторное хранилище unique_ptr
s родительским классом (подробности см. в ответе ApproachingDarknessFish),
Если все объекты относятся к фундаментальным (то есть встроенным) типам, используйте union
, который группирует типы, и определите vector<yourUnionType>
,
Если объекты неизвестного типа, но вы уверены, что они имеют схожий интерфейс, создайте базовый класс и выведите из него шаблонный дочерний класс (template <typename T> class container: public parent{};
) и создайте vector<unique_ptr<parent>>
, как в первом случае,
Если объекты относятся к типам, которые по какой-либо причине не могут быть связаны (например, vector
хранит int
, std::string
и yourType
), соедините их через union
, как в 2. Или - еще лучше...
...если у вас есть время и вы хотите чему-то научиться - посмотрите, как реализован boost::any
, и попробуйте реализовать его самостоятельно, если уж совсем не хочется пользоваться самой библиотекой. Это не так сложно, как может показаться.
vector
не особенно удобно - вам нужно дополнительно хранить вторичный вектор, который будет содержать тип, который хранится в каждом объединении, чтобы вы правильно обращались к нему и не получали мусор.
- person Paweł Stawarz; 06.10.2014
вы можете использовать std::any, хранить свои объекты в векторе как любые, а когда вы их вытаскиваете, используйте type() == typeid(mytype)
https://en.cppreference.com/w/cpp/utility/any
Это только для С++ 17 и выше.
Это должен быть вектор? Вы можете просто рассмотреть связанный список универсального типа, затем перебрать этот список, использовать typeid(), чтобы выяснить тип данных узла, и получить данные с помощью функции node.get().
tuple
. Векторы (также известные как списки) представляют собой последовательности из одного или нескольких однородных элементов. Кортежи — это конечные (и фиксированного размера) наборы возможных разнородных элементов. - person user2864740   schedule 06.10.2014