Опитвам се да направя вектор в C++, който може да съхранява 3 различни типа данни. Не искам да използвам библиотеката за усилване. Нещо като:
vector<type1, type2, type3> vectorName;
Трябва ли да направя шаблон? И ако да, как бих направил това?
Опитвам се да направя вектор в C++, който може да съхранява 3 различни типа данни. Не искам да използвам библиотеката за усилване. Нещо като:
vector<type1, type2, type3> vectorName;
Трябва ли да направя шаблон? И ако да, как бих направил това?
РЕДАКТИРАНЕ: от C++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;
Опитвам се да направя вектор в C++, който може да съхранява 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
Това обаче е само за C++17 нататък.
Трябва ли да е вектор? Може просто да помислите за свързан списък от общ тип, след което да преминете през този списък, да използвате typeid(), за да разберете типа данни на възела и да изтеглите данните с функция node.get().
tuple
, може би. Векторите (известни още като списъци) са последователности от един или повече хомогенни елементи. Кортежите са ограничени (и с фиксиран размер) колекции от възможни разнородни членове. - person user2864740   schedule 06.10.2014