Here's a few lines from my code:
class Base;
class DerivedA : public Base;
class DerivedB : public Base;
class DerivedC : public Base;
std::vector<Base*> vector_of_base;
vector_of_base.push_back(new Base);
vector_of_base.push_back(new DerivedA);
vector_of_base.push_back(new DerivedB);
vector_of_base.push_back(new DerivedC);
// input from the user
std::vector<std::size_t> user_defined_index;
std::vector<Base*> new_vector;
for(auto index: user_defined_index)
{
// I have no idea if this will work as I expect
Base *object = nullptr;
object = vector_of_base.at(index);
// compare the type of object to allocate the right thing
if(typeid(object) == typeid(Base)
{
// allocate a new instance of Base
// copying values from the previous instance (although
// this is strictly speaking not necessary in my requirements)
new_vector.push_back(new Base{object});
}
else if(typeid(object) == typeid(DerivedA))
{
new_vector.push_back(new DerivedA{object}); // ?
}
// etc ...
}
There is one immediate problem is that I have a significant number of Derived*
types. (Somewhere around 20 - 30, and hence this produces a lot of duplicate code in one long if-else if-else
statement.
However, I am not actually sure if the above code will actually work. Will the typeid(object)
will always produce the value typeid(Base)
, or will it produce the "expected" value. In other words, if object
was allocated using new DerivedA
then does the typeid(object)
statement produce the same value as typid(DerivedA)
?
The code above might not be that clear, so here is a summary of what it does:
- A
vector
is allocated with every possible type ofBase
andDerived
type. Each of these types overloads aGetName()
operator which returns astd::string
. - These strings are inserted into a graphical user interface. The user selects the quantity of each type required. (May be zero.)
- A new vector must be created, and it must contain objects allocated with
new
. The type of each object to be allocated is dictated by the users selection of quantity and order of each type from the GUI.
Hopefully that makes at least some sense?
CodePudding user response:
If you only want to shorten your code, you probably want this:
#include <cassert>
#include <iostream>
#include <vector>
using namespace std;
class A {
public:
virtual void print() const { cout << "A" << endl; }
};
class B : public A {
public:
virtual void print() const override { cout << "B" << endl; }
};
vector<A *(*)()> vec;
template <typename T> void foo() {
vec.push_back([]() -> A * { return new T; });
}
int main() {
foo<A>();
foo<B>();
// Add more foo<T>()
int i = 0;
vec[i]()->print(); // prints A
i = 1;
vec[i]()->print(); // prints B
return 0;
}
CodePudding user response:
The OP doesn't specify if they can modify the class code, or whether they have to use the Base and Derived classes as given. IF the OP is in control, this is a basic clone idiom:
#include <iostream>
#include <memory>
#include <vector>
#include <string>
#define CLONE_CODE(T) \
std::unique_ptr<Base> clone() const { return std::make_unique<T>(*this); }
class Base
{
public:
virtual CLONE_CODE(Base)
virtual std::string type() const { return "Base"; };
virtual ~Base() {}
};
class DerivedA : public Base
{
public:
CLONE_CODE(DerivedA)
std::string type() const { return "DerivedA"; }
};
class DerivedB : public Base
{
public:
CLONE_CODE(DerivedB)
std::string type() const { return "DerivedB"; }
};
int main()
{
using BasePtr = std::unique_ptr<Base>;
using PtrVec = std::vector<BasePtr>;
PtrVec vecSource;
vecSource.push_back(std::make_unique<Base>());
vecSource.push_back(std::make_unique<DerivedA>());
vecSource.push_back(std::make_unique<DerivedB>());
//etc
std::vector<std::size_t> vecIndex = { 0,1,2,1,1 };
PtrVec vecCopy;
for (auto n: vecIndex)
vecCopy.push_back(vecSource[n]->clone());
//Check copied vector has the correct types
for (auto& p : vecCopy)
std::cout << p->type() << std::endl;
}
With the output:
Base
DerivedA
DerivedB
DerivedA
DerivedA
The code uses a macro which isn't great. Of course you can write the code long-hand for each derived class. There are template versions of this, involving an intermediate template class eg Forwarding Constructor with CRTP, but they get rather complex (for me anyway!).
NB.
- I've used std::unique_ptr to wrap the raw pointers.
- Assumes each class has a copy constructor available.