Я пытаюсь создать вектор на С++, который может хранить 3 разных типа данных. Я не хочу использовать библиотеку boost. Что-то вроде:
vector<type1, type2, type3> vectorName;
Нужно ли создавать шаблон? И если да, как бы я это сделал?
Я пытаюсь создать вектор на С++, который может хранить 3 разных типа данных. Я не хочу использовать библиотеку boost. Что-то вроде:
vector<type1, type2, type3> vectorName;
Нужно ли создавать шаблон? И если да, как бы я это сделал?
ОБНОВЛЕНИЕ: начиная с C++ 17, стандартная библиотека теперь включает шаблон класса std::варианта, который очень похож на ранее существующие решения в boost. variant
представляет собой безопасную альтернативу типам для объединений, которая позволяет объединять несколько типов с использованием отношения "или", например, std::variant<type1, type2, typ3>
содержит либо type1
, либо type2
, либо a 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
для родительского класса (см. ApproachingDarknessFish s для деталей),
Если все объекты являются фундаментальными (так встроенными) типами - используйте union
, который группирует типы и определяет vector<yourUnionType>
,
Если объекты неизвестного типа, но вы уверены, что они имеют сходный интерфейс, создайте базовый класс и вытащите из него шаблонный дочерний класс (template <typename T> class container: public parent{};
) и создайте vector<unique_ptr<parent>>
как в первом случае,
Если объекты относятся к типам, которые по какой-либо причине не могут быть подключены (например, vector
сохраняет int
, std::string
и yourType
), подключите их через union
, как в 2. Или - еще лучше...
... если у вас есть время и вы хотите что-то узнать - посмотрите, как реализуется boost::any
и попытайтесь реализовать его самостоятельно, если вы действительно не хотите использовать сама библиотека. Это не так сложно, как может показаться.
вы можете использовать std :: any, хранить ваши объекты в векторе как любые, а когда вы их извлекаете, используйте type() == typeid (mytype)
https://en.cppreference.com/w/cpp/utility/any
Это только для С++ 17 и выше.
Это должен быть вектор? Вы можете просто рассмотреть связанный список универсального типа, затем выполнить итерацию по этому списку, использовать typeid(), чтобы выяснить тип данных узла, и получить данные с помощью функции node.get().