mercredi 2 janvier 2019

C++ friend subclasses access private members (strategy pattern)

I've coded a crossover method for a genetic algorithm (see https://en.wikipedia.org/wiki/Crossover_(genetic_algorithm)).

The crossover method modifies the private members of the Chromosome class but I pulled it out of Chromosome into a separate pure virtual base class CrossoverStrategy (friend of Chromosome) to keep each crossover method nicely encapsulated in a subclass, i.e. the GoF strategy pattern (see https://en.wikipedia.org/wiki/Strategy_pattern).

Nw the problem is CrossoverStrategy subclasses can't access Chromosome private members because friendship isn't inherited in C++. The only 2 solutions I see are:

1) Add accessor methods to the pure virtual base class e.g. CrossoverStrategy::getGenes() to make Chromosome private members accessible to subclasses. Because CrossoverStrategy can't anticipate all the stuff its subclasses may want to do with Chromosome, I need to expose everything up front. Ugly!

2) Forward declare each CrossoverStrategy subclass and explicitly make it a friend of Chromosome. This feels slightly less ugly, at least keeps the interfaces and code cleaner. I'm leaning towards this option for aesthetics.

Any better design suggestions? Code below.

// Chromosome.h ++++++++++++++++++++++++++++++++++++++++++++++++

class CrossoverStrategy
{
public:
    virtual std::vector<Chromosome*> crossover(Chromosome *parent1, Chromosome *parent2);
    const std::vector<double> &getGenes(Chromosome *instance) { return instance != NULL ? instance->m_genes : std::vector<double>(); }; // OPTION #1 ... BOO! UGLY!
};

class CrossoverStrategyExample1; // OPTION #2 ... BOO! UGLY!

class Chromosome
{
public:
    // Friends
    friend class CrossoverStrategy;
    friend class CrossoverStrategyExample1; // OPTION #2 ... BOO! UGLY!
private:
    std::vector<double> m_genes;
};

// CrossoverStrategies.h ++++++++++++++++++++++++++++++++++++++++++++++++

#include "Chromosome.h"

class CrossoverStrategyExample1 : public CrossoverStrategy
{
public:
    virtual std::vector<Chromosome*> crossover(Chromosome *parent1, Chromosome *parent2);
private:
};

// CrossoverStrategies.cpp ++++++++++++++++++++++++++++++++++++++++++++++++

#include "CrossoverStrategies.h"

std::vector<Chromosome*> CrossoverStrategyExample1::crossover(Chromosome *parent1, Chromosome *parent2)
{
    // Do something with Chromosome private members
    // PROBLEM ... m_genes not accessible to subclasses? BOO BOO BOO!
    (for unsigned long i = 0; i < parent1->m_genes.size(); i++)
        parent1->m_genes[i] = 0.0;
}

Aucun commentaire:

Enregistrer un commentaire