I have a problem designing a class that will allow me to draw objects of various shapes.
Shape is the base class
Triangle, Square, Rectangle are derived classes from Shape class
I have a vector<Shape*> ShapeCollection that stores the derived objects i.e. Triangle,Square, Rectangle
Once I pick the an object from the vector I need to draw the object onto the screen.
At this point I am stuck at what the design of a class should be where as a single 'Drawing' class will do the drawing, consuming an object of 'Shape' class. As the vector will contain different objects of the same base class Shape. As I have a thread that picks up an object from the vector and once I have an object I must be able to draw it properly.
So more or less below is what I say
class Drawing
{
public:
void Draw(Shape* shape, string objectName)
{
// Now draw the object.
// But I need to know which Object I am drawing or use
// switch statements to identify somehow which object I have
// And then draw. I know this is very BAD!!!
// e.g.
switch(objectName)
{
case "rectangle":
DrawRectangle((Rectangle*) shape)
break;
//Rest of cases follow
}
}
}
Where as I will have a DrawSquare, DrawTriangle function which will do the drawing.
This must be something that has been solved. There must be a better way of doing this as
all this switch statement has to go away somehow!
Any guidance is much appreciated.
Thanks
#Adrian and #Jerry suggested to use virtual function, I thought of it, but I need to have my Drawing away from the base class Shape
You would use polymorphism.
Make a pure virtual function in your base class (i.e. when declaring the function assign it to 0 as in void DrawShape() = 0;)
Declare and define that function in your derived classes.
That way you can just call DrawShape() on each of these objects even if it is passed as a Shape object.
Alternatives (NOTE: code has not been tested):
Function pointer, which is like building your own vtable aka delegate.
struct square
{
void (*draw)(square&);
};
void drawSquare(square& obj)
{
// draw square code
// there is no 'this'. must access members via `obj`.
}
square s;
s.draw = drawSquare;
s.draw(s);
Functor, which is a class that overrides operator() and also is like a delegate
struct square
{
// Note that std::function can hold a function pointer as well as a functor.
function<void(square&)> draw;
};
struct drawSquare
{
void oparator()(square& obj)
{
// draw square code
// there is no 'this'. must access members via `obj`.
}
};
square s;
square s.draw = drawSquare();
s.draw(s);
NOTE: 1 and 2 can also be initialised with lambda functions:
square s;
s.draw = [](square& obj) {
// draw square code
// there is no 'this'. must access members via `obj`.
};
s.draw(s);
NOTE: 1 could be done with a template:
struct square;
template <void (*DRAW)(square&)>
struct square
{
void draw()
{
DRAW(*this);
}
};
void drawSquare(square& obj)
{
// draw square code
// there is no 'this'. must access members via `obj`.
}
square s<&drawSquare>;
s.draw();
NOTE: 2 could be done with a template as well:
template <typename DRAW>
struct square
{
void draw()
{
// First set of parentheses instantiate the DRAW object.
// The second calls the functor.
DRAW()(*this);
}
};
struct drawSquare
{
void oparator()(square& obj)
{
// draw square code
// there is no 'this'. must access members via `obj`.
}
};
square s<drawSquare>;
s.draw();
Or alternatively, which would allow the passing of a stateful functor:
template <typename DRAW>
struct square
{
DRAW draw;
};
struct drawSquare
{
void operator()(square& obj)
{
// draw square code
// there is no 'this'. must access members via `obj`.
}
};
square s<drawSquare>;
s.draw = drawSquare();
s.draw(s);
Inherit from another class that implements the function you want either with a templated base class (IIRC, this was done in the ATL). This is just rolling your own hard-coded vtable and is called the Curiously Recurring Type Pattern (CRTP).
template <class D>
struct shape
{
inline void draw() { return static_cast<D&>(*this).draw(); }
};
void draw(square& obj)
{
// draw square code
// No 'this' available. must access shape members via `obj`.
}
struct square : public D<square>
{
void draw()
{
drawSquare(*this);
}
};
Other examples can be found here and here.
Have your draw class inherit from the type of shape class which inherits from the base shape class.
struct shape
{
virtual void draw() = 0;
};
struct square : public shape
{
};
struct drawSquare : public square
{
virtual void draw()
{
// draw square code
// you access the square's public or protected members from here
}
};
Use a std::unordered_map
#include <unordered_map>
#include <typeinfo>
#include <functional>
struct shape { };
struct square : public shape { };
void drawSquare(shape& o)
{
// this will throw an exception if dynamic cast fails, but should
// never fail if called from function void draw(shape& obj).
square& obj = dynamic_cast<square&>(o);
// draw square code
// must access shape members via `obj`.
}
std::unordered_map<size_t, std::function<void(shape&)>> draw_map
{
{ type_id(square).hash(), drawSquare }
};
void draw(shape& obj)
{
// This requires the RTTI (Run-time type information) to be available.
auto it = draw_map.find(type_id(obj).hash());
if (it == draw_map.end())
throw std::exception(); // throw some exception
(*it)(obj);
}
NOTE: if you are using g++ 4.7, be warned unordered_map has been shown to have performance issues.
This is pretty much the classic demonstration of when you want a virtual function. Define a draw in your base class, then override it in each derived class. Then to draw all the objects, you step through the collection and call the draw() member for each.
class shape {
// ...
virtual void draw(canvas &c) = 0;
};
class square : public shape {
int x, y, size;
// ...
virtual void draw(canvas &c) {
c.move_to(x, y);
c.draw_to(x+size, y);
c.draw_to(x+size, y+size);
c.draw_to(x, y+size);
c.draw_to(x, y);
}
};
...and so on for each type of shape you care about.
Edit: using a strategy class, you'd end up with code vaguely along this line:
template <class draw>
class shape {
// ...
virtual void draw(canvas &c) = 0;
};
template <class d>
class square : public shape<d> {
// ...
virtual void draw(canvas &c) {
d.square(x, y, size, c);
}
};
Another possibility would be to use a Visitor pattern. This is typically used when you need/want to traverse a more complex structure instead of a simple linear sequence, but could be used here as well. This is enough more complex that it's probably a bit much to go into here, but if you search for "Visitor pattern", you should turn up a fair amount of material.
Related
Consider the following code (I know you may think this is a weird design. I'm open to constructive suggestions):
class Graphics
{
// ...
public:
template <typename Type, typename... Types>
void draw(Type const& object, Types const&... objects)
{
// ...
addToBatch(object, objects...);
// ...
}
protected:
void addToBatch(Type const& object, Types const&... objects)
{
_addToBatch(object);
if constexpr (sizeof...(objects) > 0)
addToBatch(objects...);
}
void _addToBatch(Rect rc, Brush b);
void _addToBatch(std::pair<Rect, Brush> rcAndBrush)
{
_addToBatch(rcAndBrush.first, rcAndBrush.second);
}
void _addToBatch(TextLayout const& tl, Brush b, Point origin);
// ... etc
}
class SpecializedGraphics : public Graphics
{
// ...
protected:
// here I want to implement methods for specialized complex processing,
// which will use _addToBatch methods from base class
void _addToBatch(Entry const& e);
void _addToBatch(Menu const& menu);
// etc
}
int main()
{
Rect rcClient;
Brush brush;
Entry e;
Menu menu;
// etc
SpecializedGraphics gfx;
gfx.draw(
std::make_pair(rcClient, brush),
e,
menu,
// etc
)
return 0;
}
This code gives me a compiler error. I suppose that is because Graphics::draw is the base class method and has no access to methods of the derived class. How can I properly work around this problem? One solution that comes to my mind is simply copy/paste the definition of the draw method from the base class to the derived class (name hiding). Any better suggestions? Because why do I need to define what is already defined.
You can uso pure virtual functions,
in your base class change the declaration of methods you want to left implementations to derived classes like so:
virtual void _addToBatch(Rect rc, Brush b) = 0;
by doing so the method is now declared as pure virtual,
inside your Specialized classes now you must define those functions:
class SpecializedGraphics : public Graphics
{
protected:
void _addToBatch(Rect rc, Brush b);
Here is the situation. Let's say we have a virtual base class (e.g. ShapeJuggler) which contains a method that takes a shared pointer to a virtual base class object (e.g. Shape) as argument. Let's jump into the following pseudo-code to understand:
class Shape {
}
class ShapeJuggler {
virtual void juggle(shared_ptr<Shape>) = 0;
}
// Now deriving a class from it
class Square : public Shape {
}
class SquareJuggler : public ShapeJuggler {
public:
void juggle(shared_ptr<Shape>) {
// Want to do something specific with a 'Square'
// Or transform the 'shared_ptr<Shape>' into a 'shared_ptr<Square>'
}
}
// Calling the juggle method
void main(void) {
shared_ptr<Square> square_ptr = (shared_ptr<Square>) new Square();
SquareJuggler squareJuggler;
squareJuggler.juggle(square_ptr); // how to access 'Square'-specific members?
}
make_shared or dynamic/static_cast don't seem to do the job.
Is it at all possible? Any ideas, suggestions?
Thanks
This is where std::dynamic_pointer_cast (or one of its friends) comes into play.
It's just like dynamic_cast, but for std::shared_ptrs.
In your case (assuming the Shape class is polymorphic so dynamic_cast works):
void juggle(shared_ptr<Shape> shape) {
auto const sq = std::dynamic_pointer_cast<Square>(shape);
assert(sq);
sq->squareSpecificStuff();
}
This is the multiple dispatch problem. Their are many solution to this problem, the cleanest might be using the visitor pattern, but if you just have one function that need multiple dispatch you could avoid using a visitor:
class SquareJuggler;
class TriangleJuggler;
//.... others concrete jugglers.
class Shape {
//The default behaviour for any juggler and any shape
virtual void juggle_by(Juggler& t) {
//default code for any shape an juggle
}
// list each juggler for which you may
// implement a specific behavior
virtual void juggle_by(SquareJuggler& t) {
//provides default behavior in case you will not
// create a specific behavior for a specific shape.
//for example, just call the unspecific juggler:
this->Shape::juggle_by(static_cast<Juggler&>(t));
}
virtual void juggle_by(TriangleJuggler& t) {
//provides default behavior in case you will not
//create a specific behavior for a specific shape.
//for example, just call the unspecific juggler:
this->Shape::juggle_by(static_cast<Juggler&>(t));
}
//...
};
// Now deriving a class from it
class Square : public Shape {
void juggle_by(SquareJuggler& s) override{
//code specific to SquareJuggler and Shape
}
};
class Triangle : public Shape {
void juggle_by(TriangleJuggler& t) override{
//code specific to TriangleJuggler and Shape
}
};
class ShapeJuggler {
virtual void juggle(shared_ptr<Shape> s) {
//by default (if default has sense):
s->juggle_by(*this);
}
};
class SquareJuggler: public ShapeJuggler {
public:
void juggle(shared_ptr<Shape> s) override {
s->juggle_by(*this);
}
};
class TriangleJuggler: public ShapeJuggler {
public:
void juggle(shared_ptr<Shape> s) override {
s->juggle_by(*this);
}
};
// Calling the juggle method
void main(void) {
shared_ptr<Square> square_ptr = (shared_ptr<Square>) new Square();
SquareJuggler squareJuggler;
squareJuggler.juggle(square_ptr);
//This last call, will perform two virtual calls:
// 1. SquareJuggler::juggle(shared_ptr<Shape);
// 2. Square::juggle_by(SquareJuggler&);
}
You could also defines your XXXJuggler as final, which will enable some devirtualization optimization.
I read some of the answers in What is the proper use case for dynamic_cast.
The line which best matched my situation here is
#include<iostream>
class Shape
{
public:
virtual void draw()=0;
virtual ~Shape(){};
};
class Rectangle : public Shape
{
public:
int length;
int breath;
void draw()
{
std::cout<<"RECTANGE"<<std::endl;
}
};
class Circle : public Shape
{
public:
int diameter;
void draw()
{
std::cout<<"CIRCLE"<<std::endl;
}
};
/*Abstract Factory*/
Shape* getShapeObj(int type)
{
switch(type)
{
case 1:
return new Rectangle;
case 2:
return new Circle;
/* many types will be added here in future. */
}
return NULL;
};
void drawShapes(Shape *p_shape[],int len)
{
for(int i=0;i<len;i++)
p_shape[i]->draw();
}
int main()
{
Shape *l_shape[2];
l_shape[0]=getShapeObj(1);
l_shape[1]=getShapeObj(2);
Rectangle *l_rec=dynamic_cast<Rectangle*>(l_shape[0]);
if(l_rec)
{
l_rec->length=10;
l_rec->breath=20;
}
Circle *l_circle=dynamic_cast<Circle*>(l_shape[1]);
if(l_circle)
l_circle->diameter=25;
drawShapes(l_shape,2);
}
Essentially, virtual functions only work in some cases, not all of them.
My problem is to pass the input for the virtual function and inputs will vary from type to type. Whether using dynamic cast is recommended here?
The solution is perfect forwarding of function parameters, introduced in c++11.
template<typename ...CtorArgs>
Shape* getShapeObj(int type, CtorArgs&& ctor_args...)
{
switch(type)
{
case 1:
return new Rectangle(std::forward<CtorArgs>(ctor_args)...);
// many types will be added here in future.
}
return NULL;
}
Obviously making the function a template, defeats the purpose of hiding the hierarchy (as well as forcing rather strict requirements on the number of parameters to the constructors). But if the base contains a map of functions that do the construction, which each derived class updates with a pointer to function that constructs it, you can still have information hiding.
I have recently written an answer about storing type erased function pointers in a map, with some static type checking forwarded to run time.
In this particular case, looks like your main function is taking too much responsibility. What if you have Circle, Hexagon, MyFancyFigure types? All of them should be initialized in main in different branches?
It would be much better to move that "initialization" logic to a separate virtual function init in your classes (or even to the constructor). The code would look like this:
class Shape
{
public:
virtual void draw()=0;
virtual void init()=0;
virtual ~Shape(){};
};
class Rectangle : public Shape
{
public:
int length;
int breath;
void draw()
{
//Draw Rectangle
}
void init()
{
length = 10;
breath = 20;
}
};
int main()
{
Shape *l_shape=getShapeObj(1);
// Calls different code of "init" method depending on the actual object type
l_shape->init();
l_shape->draw();
delete l_shape;
}
Also, please note that this initialization logic may be place in some other place, like constructor of the class or the factory method. But main is definitely the wrong place.
I have several similar classes inheriting from the same Base-Class/Interface (Base class 1), and they share a couple similar functions, but then also have their own distinct functions. They all also have their own member variables of different classes, and each of those inherits from the same Base-Class/Interface (Base class 2). Is it possible to define a variable in Base class 1, of type Base class 2, then in the actual implementation of classes using Base class 1, have the variable of type Base class 2 be its proper type. Kinda hard to explain, so simplified example below.
//Base-Class 1
class Shape
{
public Shape() {}
ShapeExtra m_var;
//The common functions
public GetVar(){ return m_var; }
}
class Circle : Shape
{
public Circle() { m_var = new CircleExtra(); }
public void CircleFunc()
{
m_var.CircleExtraFunc();
}
}
class Triangle : Shape
{
public Triangle() { m_var = new TriangleExtra(); }
public void TriangleFunc()
{
m_var.TriangleExtraFunc();
}
}
.
.
.
//Base_Class 2
class ShapeExtra
{
public ShapeExtra() {}
}
class CircleExtra : ExtraClass
{
public CircleExtra() {}
void CircleExtraFunc() {//Do stuff}
}
class TriangleExtra : ExtraClass
{
public TriangleExtra() {}
void TriangleExtra() {//Do stuff}
}
.
.
.
So, I need the m_var in the child classes to be kept it as its own unique version. Because right now (w/o the extra CircleExtra m_var;), the GetVar() works, but in CircleFunc, m_var is still type of ShapeExtra, and thus doesn't know that CircleExtraFunc exists. I could cast m_var each time I wanted to do that, but that is repetitive and not worth it in my real-world case. Is there a way to utilize the functions in unique classes based off of ShapeExtra, while keeping the GetVar() function in Shape?
Please ask questions if there is anything I left out.
Simply with inheritance and without using pointers it is not possible, as C++ is a statically-and-strictly-typed language.
You can inherit both the variable and the function, but you'll need to cast function return value.
You can also override the function to make it return the concrete type, but then you have to cast the variable inside the function.
You can also declare the same var with the concrete class in subclasses, but then you just hide the variable in the superclass and inherit nothing.
I'd rather go for a solution using templates. Make the type of the variable a template type and extend the template using a concrete type in subclasses. It'll work perfectly.
It's been a long time since I last programmed in C++ and I beg your pardon if there are errors in the following example. I'm sure you can easily make it work.
template <class S>
class Shape {
S m_var;
//......
public:
S var () {
return m_var;
}
//.......
}
class Circle: Shape <CircleExtra> {
// var method returns CircleExtra
//......
}
Edit:
Regarding some comment, to allow virtual invocation of the method, it is possible to use correlated return types. Something like the following example.
class Shape {
public:
virtual ShapeExtra *var () = 0;
}
template <typename SE>
class ConcreteShape: Shape {
public:
virtual SE *var() {
return &m_var;
}
// Constructor, etc.
private:
SE m_var;
}
Or some variation. Now concrete shapes can benefit from extending the template, as long as SE * is correlated with ShapeExtra * (the type parameter extends ShapeExtra). And you can vall the method transparently through Shape interface.
Using pointers, this is totally possible.
Using your example, you could do something like this:
#include <iostream>
#include <memory>
using namespace std;
//Extras
class ShapeExtra
{
public:
ShapeExtra() {}
void ShapeFunc() { std::cout << "Shape"; }
virtual ~ShapeExtra() = default; //Important!
};
class Shape
{
public:
std::unique_ptr<ShapeExtra> m_var;
//require a pointer on construction
//make sure to document, that Shape class takes ownership and handles deletion
Shape(ShapeExtra* p):m_var(p){}
//The common functions
ShapeExtra& GetVar(){ return *m_var; }
void ShapeFunc() {m_var->ShapeFunc();}
};
class CircleExtra : public ShapeExtra
{
public:
void CircleExtraFunc() {std::cout << "Circle";}
};
class Circle : public Shape
{
CircleExtra* m_var;
public:
Circle() : Shape(new CircleExtra()) {
m_var = static_cast<CircleExtra*>(Shape::m_var.get());
}
void CircleFunc()
{
m_var->CircleExtraFunc();
}
};
int main() {
Circle c;
//use the ShapeExtra Object
c.GetVar().ShapeFunc();
//call via forwarded function
c.ShapeFunc();
//call the circleExtra Function
c.CircleFunc();
return 0;
}
Test it on ideone
Note the use of pointers and a virtual destructor:
By using a virtual destructor in the ShapeExtra base class, you make it possible to destruct an object of any derived class, using a ShapeExtra*. This is important, because
by using a std::unique_ptr<ShapeExtra> instead of a plain C-pointer, we make sure that the object is properly deleted on destruction of Shape.
It is probably a good idea to document this behaviour, i.e. that Shape takes the ownership of the ShapeExtra*. Which especially means, that we do not delete CirleExtra* in the Circle destructor
I decided here to require the ShapeExtra* on construction, but its also possible to just use std::unique_ptr::reset() later and check for nullptr on dereferencing Shape::m_var
Construction order is this: On calling the constructor of Circle, we first create a new CircleExtra which we pass to Shape before finally the constructor of Circle is executed.
Destruction order is Circle first (was created last), then Shape which also destructs the ShapeExtra for us, including (via virtual function) the CircleExtra
I would recommend the following approach:
class ShapeExtra
{
public:
virtual ~ShapeExtra() { }
virtual void SomeCommonShapeFunc() { std::cout << "Shape"; }
};
class Shape
{
public:
virtual ShapeExtra &GetVar() = 0; // Accessor function.
};
Note that the class Shape does not have any data members at all. After that for each derived class you need:
class CircleExtra : public ShapeExtra
{
public:
void SomeCommonShapeFunc() { std::cout << "Circle"; }
};
class Circle : public Shape
{
CircleExtra m_var; // Data member with circle specific class.
public:
virtual ShapeExtra &GetVar() { return m_var; }
};
Implementation of virtual method in Circle will return reference to the base class ShapeExtra. This will allow using this extra in the base class.
Note that pointers and templates are not used at all. This simplifies the overall design.
I'm coding a game engine and I have this class set up for objects:
class SceneManager //controls everything in the "world" game
{
public:
void Add(SceneObject* object); //adds to the vector
private:
vector<SceneObject*> _worldObjects; //the vector that contains all of them
}
And all classes I work on the game inherit from SceneObject:
class SceneObject
{
public:
virtual void Draw() = 0;
}
class Image : public SceneObject
{ }
class Sprite : public SceneObject
{ }
class Model3D : public SceneObject
{ }
So I know I can call Draw() for all objects in my vector.
But I've been working on optimizations and I'm trying to get rid of all inheritance and virtual functions, and use composition instead, since they can't be inlined and seems to be a major performance issue when performed on a per-object basis.
I'm looking for some C++ technique that I can use to be able to store a bunch of SceneObjects in my vector, and then call Draw() on it and it properly draws the object related to it. This will also work for the Update() function I'm using as virtual.
So this code:
void SceneManager::Add(SceneObject* object)
{
_worldObjects.push_back(object);
}
void SceneManager::DrawTheWorld()
{
for(unsigned int i = 0; i < _worldObjects.size(); i++)
{
_worldObjects[i]->Draw(); //SceneObject's being called
}
}
...would become:
void SceneManager::Add(Image* image)
{
SceneObject* object = new SceneObject();
//link object to image somehow, tried to use it as a member of image
_worldObjects.push_back(object);
}
void SceneManager::DrawTheWorld()
{
for(unsigned int i = 0; i < _worldObjects.size(); i++)
{
//_worldObjects[i]->
//I need somehow to be able to get the pointer back to the original class
//It can be an image, sprite, model3d, anything
}
}
I don't think if I add a switch or if/elses and removing the virtual I'd gain any performance, so I'm trying to figure if there's a clean way to deal with this.
Any ideas?
You can use free functions to model the drawable aspect of your objects:
#include <iostream>
class Image { };
class Sprite { };
class Model3D { };
namespace draw_aspect
{
void draw(Image const& image) { std::cout << "drawing image\n"; }
void draw(Sprite const& sprite) { std::cout << "drawing sprite\n"; }
void draw(Model3D const& model3D) { std::cout << "drawing model3D\n"; }
}
Now, either use three separate vectors (this could well be most optimal, depending on the ordering relationship between the objects across collections?), or consider a variant type vector:
1. Using variant types
#include <boost/variant.hpp>
using SceneObject = boost::variant<Image, Sprite, Model3D>;
namespace draw_aspect {
struct draw_visitor : boost::static_visitor<> {
template <typename T> void operator()(T const& t) const { draw(t); }
};
void draw(SceneObject const& sobj) {
static const draw_visitor _vis;
boost::apply_visitor(_vis, sobj);
}
}
A complete proof of concept of the latter: Live on Coliru
#include <vector>
class SceneManager //controls everything in the "world" game
{
public:
void Add(SceneObject v) { _worldObjects.emplace_back(std::move(v)); }
friend void draw(SceneManager const& sm) { return sm.draw(); }
private:
void draw() const {
for(auto& sobj : _worldObjects)
draw_aspect::draw(sobj);
}
std::vector<SceneObject> _worldObjects; //the vector that contains all of them
};
int main()
{
SceneManager sman;
sman.Add(Image());
sman.Add(Sprite());
sman.Add(Model3D());
sman.Add(Image());
draw(sman);
}
Outputs
drawing image
drawing sprite
drawing model3D
drawing image
2. Separate collections
The alternative using separate vectors: Live on Coliru
class SceneManager //controls everything in the "world" game
{
public:
void Add(Image v) { _images .emplace_back(std::move(v)); }
void Add(Sprite v) { _sprites .emplace_back(std::move(v)); }
void Add(Model3D v) { _model3Ds.emplace_back(std::move(v)); }
friend void draw(SceneManager const& sm) { return sm.draw(); }
private:
void draw() const {
for(auto& sobj : _images) draw_aspect::draw(sobj);
for(auto& sobj : _sprites) draw_aspect::draw(sobj);
for(auto& sobj : _model3Ds) draw_aspect::draw(sobj);
}
std::vector<Image> _images;
std::vector<Sprite> _sprites;
std::vector<Model3D> _model3Ds;
};
int main()
{
SceneManager sman;
sman.Add(Image());
sman.Add(Sprite());
sman.Add(Model3D());
sman.Add(Image());
draw(sman);
}
Note that the output is different (ordering):
drawing image
drawing image
drawing sprite
drawing model3D
Solving your specific petition is one thing that others have already done.
However, I think you should take a step back and consider the whole picture. Is this a wise step to take? Any possible alternative to virtual functions will introduce maintainability problems, i.e., difficulty to modify and even to understand code.
The question is: is this really necessary? Will it really compensate?
Virtual functions involve derreferencing two pointers instead of only one. And yes, it is true it won't be inlined. I don't think, however, this being a real issue. I would indeed concentrate in algorithm-level optimization, and waste all other approaches before removing virtual funcions.
Take into account that at least one solution involves converting virtual functions to regular functions (not member functions), removing the well-known advantage of a virtual function (i.e., the class of the object itself) vs. a chain of if's.
That's said, it is your call.
Since you seem to have a fixed number types, it seems a reasonable approach would be the use of one vector per type and applying the operations separately for each type: processing a sequence of heterogeneous objects will amount to some disruption whether it is using virtual functions are not. Putting the framework of how the respective objects are called into a function template will conveniently deal with the commonality.