Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ inheritance: Templates Vs Pointers

I am trying to understand what is the best solution between pointers and templates in a inheritance scenario.

Consider the following classes.

class Event
{
};

class Filter
{
    public:
        virtual void process(Event *event) = 0;
};

class Pipeline
{
    private:
        std::vector<Filter*> _filters
};

Each user can expand the Event class and the Filter class to hold actual data and actual filtering functions. The pipeline class only connects the filters together with queues and executes the method process.

UP until now I always used pointers to deal with inheritance, e.g. a std::vector of Filter pointers and a process function that receives an Event pointer. Instead of pointers can template be used?

For example

class Event
{
};

template<class Event> class Filter
{
    public:
        virtual void process(Event *event) = 0;
};

template<class Filer> class Pipeline
{
    private:
        std::vector<Filter> _filters
};

Does this work, is even possible and what are the main implications?

I have another example to discuss. Consider the following code:

template<class Element, class Cluster>
Cluster* closestCluster(Element *e, std::vector<Cluster*> &clusters)
{
    double minDist = clusters[0]->distance(e);
    Cluster *c = clusters[0];
    for(std::size_t i = 1 ; i < clusters.size(); ++i)
    {
         double tmp = clusters[i]->distance(e);
         if(tmp < minDist)
         {
             minDist = tmp;
             c=clusters[i];
         }
    }
    return c;
}

Cluster* closestCluster(Element *e, std::vector<Cluster*> & clusters)
{
    double minDist = clusters[0]->distance(e);
    Cluster *c = clusters[0];
    for(std::size_t i = 1 ; i < clusters.size(); ++i)
    {
        double tmp = clusters[i]->distance(e);
        if(tmp < minDist)
        {
            minDist = tmp;
            c=clusters[i];
        }
     }
     return c;
}

At first glance I look at this to functions and though there is no big difference. But in my code only the first one works. Because my array of clusters is of this type:

std::vector<KMedoidCluster*> &clusters

I thought the compiler could understand that KMedoidclsuter is a extension of the base class Cluster. But apparently it does not work, so in order to get some flexibility I had to use templates.

like image 319
mariolpantunes Avatar asked Oct 03 '22 10:10

mariolpantunes


2 Answers

In essence you are trading run-time polymorphism for compile-time polymorphism.

What this means is that with the second approach, a pipeline can only consist of a single type of filter, and that type must be known at compile time. The first approach is more flexible than that.

On the other hand, memory management is simpler in the second approach. However, you could use smart pointers with the first approach to simplify things.

Additionally, the second approach could potentially be more performant. Whether this is relevant is a different question.

In a nutshell, I'd use the first approach but would use smart pointers instead of raw pointers.

like image 95
NPE Avatar answered Oct 13 '22 12:10

NPE


First I think use template will work.but your code may write like this:

class Event
{
};

template<class T1> class Filter
{
    public:
        virtual void process(T1 *event) = 0;
};

 template<class T2> class Pipeline
{
    private:
        std::vector<T2> _filters
};

The main advantage of using template is that, you can set you class T1 not only Event but also other class like Event1. And you can set T2 as Filter or other class Filter1.

However in this condition, this becomes a disadvantage. You can not make sure about your Filter class can always process object of class Event. Other class such as Event1's object will also work.

Finally, I suggest you use the pointers not the template in this condition.

I hope this will help you.

like image 39
yanchong Avatar answered Oct 13 '22 12:10

yanchong