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












-1















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).



Now 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) = 0;
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;
}









share|improve this question























  • Not just ugly, fatal. Thou shall not return reference to local variable.

    – user4581301
    Jan 3 at 2:45











  • Bob is your friend. You trust him with your secrets. But what about Bob's drug-dealing dirtbag of a son, Griff? Do you automatically trust Griff because you trust Bob? Nope. If classes derived from a friendwere automatically friends, you could subclass whatever you wanted and completely smurf over encapsulation. Not a good plan.

    – user4581301
    Jan 3 at 2:52













  • If you can't possibly pass a null pointer for a pointer, consider passing by references instead of pointers. All but eliminates whole families of accidental screw-ups.

    – user4581301
    Jan 3 at 3:03











  • Given that you want multiple classes/function to have access to data that makes up a Chromosome, why isn't the data public?

    – Peter
    Jan 3 at 3:33











  • @Peter because then anybody could access the private data. I would prefer if only the friend (and its subclasses) could access. This is a good option 3 but the risk is we un-encapsulate Chromosome entirely.

    – cridgit
    Jan 3 at 3:41
















-1















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).



Now 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) = 0;
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;
}









share|improve this question























  • Not just ugly, fatal. Thou shall not return reference to local variable.

    – user4581301
    Jan 3 at 2:45











  • Bob is your friend. You trust him with your secrets. But what about Bob's drug-dealing dirtbag of a son, Griff? Do you automatically trust Griff because you trust Bob? Nope. If classes derived from a friendwere automatically friends, you could subclass whatever you wanted and completely smurf over encapsulation. Not a good plan.

    – user4581301
    Jan 3 at 2:52













  • If you can't possibly pass a null pointer for a pointer, consider passing by references instead of pointers. All but eliminates whole families of accidental screw-ups.

    – user4581301
    Jan 3 at 3:03











  • Given that you want multiple classes/function to have access to data that makes up a Chromosome, why isn't the data public?

    – Peter
    Jan 3 at 3:33











  • @Peter because then anybody could access the private data. I would prefer if only the friend (and its subclasses) could access. This is a good option 3 but the risk is we un-encapsulate Chromosome entirely.

    – cridgit
    Jan 3 at 3:41














-1












-1








-1








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).



Now 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) = 0;
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;
}









share|improve this question














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).



Now 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) = 0;
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;
}






c++ inheritance design-patterns friend






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Jan 3 at 2:05









cridgitcridgit

1238




1238













  • Not just ugly, fatal. Thou shall not return reference to local variable.

    – user4581301
    Jan 3 at 2:45











  • Bob is your friend. You trust him with your secrets. But what about Bob's drug-dealing dirtbag of a son, Griff? Do you automatically trust Griff because you trust Bob? Nope. If classes derived from a friendwere automatically friends, you could subclass whatever you wanted and completely smurf over encapsulation. Not a good plan.

    – user4581301
    Jan 3 at 2:52













  • If you can't possibly pass a null pointer for a pointer, consider passing by references instead of pointers. All but eliminates whole families of accidental screw-ups.

    – user4581301
    Jan 3 at 3:03











  • Given that you want multiple classes/function to have access to data that makes up a Chromosome, why isn't the data public?

    – Peter
    Jan 3 at 3:33











  • @Peter because then anybody could access the private data. I would prefer if only the friend (and its subclasses) could access. This is a good option 3 but the risk is we un-encapsulate Chromosome entirely.

    – cridgit
    Jan 3 at 3:41



















  • Not just ugly, fatal. Thou shall not return reference to local variable.

    – user4581301
    Jan 3 at 2:45











  • Bob is your friend. You trust him with your secrets. But what about Bob's drug-dealing dirtbag of a son, Griff? Do you automatically trust Griff because you trust Bob? Nope. If classes derived from a friendwere automatically friends, you could subclass whatever you wanted and completely smurf over encapsulation. Not a good plan.

    – user4581301
    Jan 3 at 2:52













  • If you can't possibly pass a null pointer for a pointer, consider passing by references instead of pointers. All but eliminates whole families of accidental screw-ups.

    – user4581301
    Jan 3 at 3:03











  • Given that you want multiple classes/function to have access to data that makes up a Chromosome, why isn't the data public?

    – Peter
    Jan 3 at 3:33











  • @Peter because then anybody could access the private data. I would prefer if only the friend (and its subclasses) could access. This is a good option 3 but the risk is we un-encapsulate Chromosome entirely.

    – cridgit
    Jan 3 at 3:41

















Not just ugly, fatal. Thou shall not return reference to local variable.

– user4581301
Jan 3 at 2:45





Not just ugly, fatal. Thou shall not return reference to local variable.

– user4581301
Jan 3 at 2:45













Bob is your friend. You trust him with your secrets. But what about Bob's drug-dealing dirtbag of a son, Griff? Do you automatically trust Griff because you trust Bob? Nope. If classes derived from a friendwere automatically friends, you could subclass whatever you wanted and completely smurf over encapsulation. Not a good plan.

– user4581301
Jan 3 at 2:52







Bob is your friend. You trust him with your secrets. But what about Bob's drug-dealing dirtbag of a son, Griff? Do you automatically trust Griff because you trust Bob? Nope. If classes derived from a friendwere automatically friends, you could subclass whatever you wanted and completely smurf over encapsulation. Not a good plan.

– user4581301
Jan 3 at 2:52















If you can't possibly pass a null pointer for a pointer, consider passing by references instead of pointers. All but eliminates whole families of accidental screw-ups.

– user4581301
Jan 3 at 3:03





If you can't possibly pass a null pointer for a pointer, consider passing by references instead of pointers. All but eliminates whole families of accidental screw-ups.

– user4581301
Jan 3 at 3:03













Given that you want multiple classes/function to have access to data that makes up a Chromosome, why isn't the data public?

– Peter
Jan 3 at 3:33





Given that you want multiple classes/function to have access to data that makes up a Chromosome, why isn't the data public?

– Peter
Jan 3 at 3:33













@Peter because then anybody could access the private data. I would prefer if only the friend (and its subclasses) could access. This is a good option 3 but the risk is we un-encapsulate Chromosome entirely.

– cridgit
Jan 3 at 3:41





@Peter because then anybody could access the private data. I would prefer if only the friend (and its subclasses) could access. This is a good option 3 but the risk is we un-encapsulate Chromosome entirely.

– cridgit
Jan 3 at 3:41












3 Answers
3






active

oldest

votes


















2














Option 2 should be rejected because it does not scale. You will be continually modifying Chromosome to keep it up to date with new CrossoverStrategies.



Option 1 is a strange idea because it places the getter function for Chromosome's data members outside of Chromosome. I can see some cases where this is an attractive idea, if getGenes is made protected, but I'm not convinced here. Consider instead



Option 1-A



class Chromosome
{
public:
const std::vector<double>& getGenes() const
{
return m_genes;
}
private:
std::vector<double> m_genes;
};


Everyone who can access a Chromosome can access getGenes, but they can't do anything to damage it and Chromosome remains blissfully ignorant of its users.



Option 3: Use The Pimpl Idiom



Short and stupid example with a few flaws to keep the demo short



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



#include <vector>
class Chromosome; // forward declaration only
class CrossoverStrategy
{
public:
virtual ~CrossoverStrategy() = default;
virtual std::vector<Chromosome*> crossover(Chromosome *parent1, Chromosome *parent2) = 0;
};

Chromosome * ChromosomeFactory(/* some construction parameters here */);
// should also provide a declaration of a factory function to provide CrossoverStrategies


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"
class Chromosome
{
public:
std::vector<double> m_genes;

// silence a warning
Chromosome(): m_genes{}
{

}
};

// Because Chromosome is only defined in this file, only this file can use the internals
// of Chromosome. They are public, but the outside world doesn't know that

Chromosome * ChromosomeFactory(/* some construction parameters here */)
{
// Probably makes and returns a pointer to a Chromosome,
// but could pull it from a list, copy construct from a template, etc...
return new Chromosome(/* some construction parameters here */);
}

// should also provide a definition of a factory function to provide CrossoverStrategies


std::vector<Chromosome*> CrossoverStrategyExample1::crossover(Chromosome *parent1,
Chromosome *parent2)
{
for (unsigned long i = 0; i < parent1->m_genes.size(); i++)
parent1->m_genes[i] = 0.0;
return std::vector<Chromosome*>{}; // silence a warning
}


Main.cpp ++++++++++++++++++++++++++++++++++++++++++++++++



#include "Chromosome.h"
#include "CrossoverStrategies.h" // A bad idea. Forces recompilation when strategies are added

int main()
{
Chromosome * p1 = ChromosomeFactory(/* some construction parameters here */);
p1->m_genes.push_back(0.0); // will fail to compile (incomplete type)
Chromosome * p2 = ChromosomeFactory(/* some construction parameters here */);

// probably should hide the next line with a factory as well
CrossoverStrategy * strategy = new CrossoverStrategyExample1();
strategy->crossover(p1, p2);
}


A quick afterword on security. It always comes at a cost. Generally it makes things harder to use. It makes them harder for an attacker, but it also makes things harder for the legitimate users. Whether it's worth it or not is up to you.






share|improve this answer


























  • Thank you for the suggestions. I'm aware of the fact that I'd need to continually modify Chromosome.h when I add a new strategy. But that is a known limitation and (only) 2 lines of code need to change, so it seems like an acceptable tradeoff for the cleanliness of the code.

    – cridgit
    Jan 3 at 3:33











  • Regarding your option 1 suggestion: the whole purpose is to provide non-const access to Chromosome's private members. If we make the getter non-const then any random class could play with Chromsome's privates. Obviously I want to restrict this access only to CrossoverStrategy and its subclasses. Remember, in effect we're taking what was originally Chromosome::crossover() and externalizing it so we can swap out with different strategies. JavaScript does this beautifully with anonymous functions but I find C++ lamdbas ugly and unreadable by comparison.

    – cridgit
    Jan 3 at 3:37











  • @cridgit What sorts of things can non-CrossoverStrategies do with Chromsome? If the answer is nothing, see if making Chromsome public and applying the pimpl idiom makes sense.

    – user4581301
    Jan 3 at 5:53











  • The code includes an example of what it needs to do with Chromosome privates: "parent1->m_genes[i] = 0.0;" It needs to access and modify this and perhaps other private members. I looked at pimpl but that also appears to completely hide the private members, so I'm not sure how I'd be able to achieve the desired outcome with pimpl, but thank you for the suggestion.

    – cridgit
    Jan 3 at 8:23






  • 1





    I've figured out how to do this using your example above based on the Pimpl pattern and it is exactly what I was looking for. THANK YOU. I used a public ChromosomeImpl variable in the header file. Then I made the ChromosomeImpl members public in the source file so only the CrossoverStrategy subclasses have access to them. Works beautifully!

    – cridgit
    Jan 4 at 12:44



















2














The first, obvious, option is to consider whether the members of Chromosome should or should not be public. Given that you want an arbitrary number of classes to have access to its data, an obvious option is to make that data public.



A second option is for Chromosome to provide a public getter and setter for the affected data, such as;



 class Chromosome
{
public:

std::vector<double> getGenes() const {return m_genes;};
bool setGenes(const std::vector<double> &newgenes)
{
bool is_error = true;
if (IsValid(newgnes))
{
is_error = false;
m_genes = newgenes;
}
return is_error; // return true if change is rejected
};

private:

std::vector<double> m_genes;
};


Then all CrossOverStrategy and its derived classes need to do, given valid pointers to Chromosomes, is request the genes, do whatever is needed, and (when done) provide a new set of genes back to selected Chromosomes.



Encapsulation of Chromosome is preserved by various measures, since the only way to change genes is via a member function of Chromosome i.e. there is no way of changing genes in a chromosome outside control of the Chromosome class. Which allows Chromosome to do any checks it likes, and reject bad genes if desired.



There is no need for any other class or function to be a friend of Chromosome. A key advantage is that it is not necessary to change the Chromosome class whenever a new class is derived from CrossOverStrategy.



The trade-off is that genes are retrieved and changes by copying the complete set (potential performance hit of copying). But it avoids the need to break encapsulation of the Chromosome class by providing, directly or indirectly, a reference to its private members to any other classes.



If copying the complete set of chromosomes is a bad thing, work out some additional member functions of Chromosome that allow the caller to request partial changes (e.g. update particular genes, insert a set of genes into a specified place in the vector of genes, etc). These additional functions need to work on the same principle: all changes of genes within a Chromosome go via member functions of Chromosome, and there is no "back door" mechanism for other code to sneak changes through.



If you really want, you can make the setter and getter private members of Chromosome, and make only the base class CrossOverStrategy a friend. Then all CrossOverStrategy needs to do is provide protected helpers that only call the private helpers of Chromosome.



class CrossoverStrategy
{
public:
virtual std::vector<Chromosome*> crossover(Chromosome *parent1, Chromosome *parent2) = 0;

protected:

std::vector<double> getGenes(Chromosome *instance)
{
return instance ? instance->getGenes() : std::vector<double>();
};

bool setGenes(Chromosome *instance, const std::vector<double> &newgenes)
{
return instance ? instance->setGenes(newgenes)) : true; // true indicates error
};
};


That way, only the classes derived from CrossOverStrategy can access the protected helpers. If the workings of Chromosome change, then the only class that needs to be adapted in this case is the base CrossOverStrategy class - since its derived classes don't (directly) access Chromosome at all.






share|improve this answer


























  • Peter thank you for this solid answer, it seems to be the most "correct" way and certainly not as ugly as the 2 options I suggested. I'll wait to see if there are any other brilliant ideas before accepting this as the answer.

    – cridgit
    Jan 3 at 13:38



















0














Your idea is fundamentally flawed.



On one hand, you say you don't want just anyone to be able to mess with the vector of genes.



On the other hand, you want any descendant of CrossoverStrategy to be able to mess with the vector of genes.



But there's a contradiction. "Any descendant" of a class is "just anyone". Any user is able to inherit from any class and do what they want with your vector of genes. They only need to go through a small one-time inconvenience of inheriting from CrossoverStrategy.



This is the reason why friendship in C++ is not inherited. If it were, access control would be useless in presence of friend classes.



Of course you can simulate inheritable friendship by having a protected getter in CrossoverStrategy as one of the answers suggests. But doing that defeats the purpose of access control. It makes the arrays of genes as good as public.






share|improve this answer


























  • While I agree that making private variables public is to be avoided except in specific circumstances, encapsulation is not binary but a spectrum. Being able to access member variables by subclassing a friend is one step better than 100% public access. So I don't agree that it entirely defeats the purpose of access control - the concept has some merit above just making everything public.

    – cridgit
    Jan 4 at 3:21











  • @cridgit I don't see the spectrum. Access is either open to all or closed. There is no half-open or almost closed.

    – n.m.
    Jan 4 at 7:15











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54015468%2fc-friend-subclasses-access-private-members-strategy-pattern%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























3 Answers
3






active

oldest

votes








3 Answers
3






active

oldest

votes









active

oldest

votes






active

oldest

votes









2














Option 2 should be rejected because it does not scale. You will be continually modifying Chromosome to keep it up to date with new CrossoverStrategies.



Option 1 is a strange idea because it places the getter function for Chromosome's data members outside of Chromosome. I can see some cases where this is an attractive idea, if getGenes is made protected, but I'm not convinced here. Consider instead



Option 1-A



class Chromosome
{
public:
const std::vector<double>& getGenes() const
{
return m_genes;
}
private:
std::vector<double> m_genes;
};


Everyone who can access a Chromosome can access getGenes, but they can't do anything to damage it and Chromosome remains blissfully ignorant of its users.



Option 3: Use The Pimpl Idiom



Short and stupid example with a few flaws to keep the demo short



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



#include <vector>
class Chromosome; // forward declaration only
class CrossoverStrategy
{
public:
virtual ~CrossoverStrategy() = default;
virtual std::vector<Chromosome*> crossover(Chromosome *parent1, Chromosome *parent2) = 0;
};

Chromosome * ChromosomeFactory(/* some construction parameters here */);
// should also provide a declaration of a factory function to provide CrossoverStrategies


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"
class Chromosome
{
public:
std::vector<double> m_genes;

// silence a warning
Chromosome(): m_genes{}
{

}
};

// Because Chromosome is only defined in this file, only this file can use the internals
// of Chromosome. They are public, but the outside world doesn't know that

Chromosome * ChromosomeFactory(/* some construction parameters here */)
{
// Probably makes and returns a pointer to a Chromosome,
// but could pull it from a list, copy construct from a template, etc...
return new Chromosome(/* some construction parameters here */);
}

// should also provide a definition of a factory function to provide CrossoverStrategies


std::vector<Chromosome*> CrossoverStrategyExample1::crossover(Chromosome *parent1,
Chromosome *parent2)
{
for (unsigned long i = 0; i < parent1->m_genes.size(); i++)
parent1->m_genes[i] = 0.0;
return std::vector<Chromosome*>{}; // silence a warning
}


Main.cpp ++++++++++++++++++++++++++++++++++++++++++++++++



#include "Chromosome.h"
#include "CrossoverStrategies.h" // A bad idea. Forces recompilation when strategies are added

int main()
{
Chromosome * p1 = ChromosomeFactory(/* some construction parameters here */);
p1->m_genes.push_back(0.0); // will fail to compile (incomplete type)
Chromosome * p2 = ChromosomeFactory(/* some construction parameters here */);

// probably should hide the next line with a factory as well
CrossoverStrategy * strategy = new CrossoverStrategyExample1();
strategy->crossover(p1, p2);
}


A quick afterword on security. It always comes at a cost. Generally it makes things harder to use. It makes them harder for an attacker, but it also makes things harder for the legitimate users. Whether it's worth it or not is up to you.






share|improve this answer


























  • Thank you for the suggestions. I'm aware of the fact that I'd need to continually modify Chromosome.h when I add a new strategy. But that is a known limitation and (only) 2 lines of code need to change, so it seems like an acceptable tradeoff for the cleanliness of the code.

    – cridgit
    Jan 3 at 3:33











  • Regarding your option 1 suggestion: the whole purpose is to provide non-const access to Chromosome's private members. If we make the getter non-const then any random class could play with Chromsome's privates. Obviously I want to restrict this access only to CrossoverStrategy and its subclasses. Remember, in effect we're taking what was originally Chromosome::crossover() and externalizing it so we can swap out with different strategies. JavaScript does this beautifully with anonymous functions but I find C++ lamdbas ugly and unreadable by comparison.

    – cridgit
    Jan 3 at 3:37











  • @cridgit What sorts of things can non-CrossoverStrategies do with Chromsome? If the answer is nothing, see if making Chromsome public and applying the pimpl idiom makes sense.

    – user4581301
    Jan 3 at 5:53











  • The code includes an example of what it needs to do with Chromosome privates: "parent1->m_genes[i] = 0.0;" It needs to access and modify this and perhaps other private members. I looked at pimpl but that also appears to completely hide the private members, so I'm not sure how I'd be able to achieve the desired outcome with pimpl, but thank you for the suggestion.

    – cridgit
    Jan 3 at 8:23






  • 1





    I've figured out how to do this using your example above based on the Pimpl pattern and it is exactly what I was looking for. THANK YOU. I used a public ChromosomeImpl variable in the header file. Then I made the ChromosomeImpl members public in the source file so only the CrossoverStrategy subclasses have access to them. Works beautifully!

    – cridgit
    Jan 4 at 12:44
















2














Option 2 should be rejected because it does not scale. You will be continually modifying Chromosome to keep it up to date with new CrossoverStrategies.



Option 1 is a strange idea because it places the getter function for Chromosome's data members outside of Chromosome. I can see some cases where this is an attractive idea, if getGenes is made protected, but I'm not convinced here. Consider instead



Option 1-A



class Chromosome
{
public:
const std::vector<double>& getGenes() const
{
return m_genes;
}
private:
std::vector<double> m_genes;
};


Everyone who can access a Chromosome can access getGenes, but they can't do anything to damage it and Chromosome remains blissfully ignorant of its users.



Option 3: Use The Pimpl Idiom



Short and stupid example with a few flaws to keep the demo short



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



#include <vector>
class Chromosome; // forward declaration only
class CrossoverStrategy
{
public:
virtual ~CrossoverStrategy() = default;
virtual std::vector<Chromosome*> crossover(Chromosome *parent1, Chromosome *parent2) = 0;
};

Chromosome * ChromosomeFactory(/* some construction parameters here */);
// should also provide a declaration of a factory function to provide CrossoverStrategies


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"
class Chromosome
{
public:
std::vector<double> m_genes;

// silence a warning
Chromosome(): m_genes{}
{

}
};

// Because Chromosome is only defined in this file, only this file can use the internals
// of Chromosome. They are public, but the outside world doesn't know that

Chromosome * ChromosomeFactory(/* some construction parameters here */)
{
// Probably makes and returns a pointer to a Chromosome,
// but could pull it from a list, copy construct from a template, etc...
return new Chromosome(/* some construction parameters here */);
}

// should also provide a definition of a factory function to provide CrossoverStrategies


std::vector<Chromosome*> CrossoverStrategyExample1::crossover(Chromosome *parent1,
Chromosome *parent2)
{
for (unsigned long i = 0; i < parent1->m_genes.size(); i++)
parent1->m_genes[i] = 0.0;
return std::vector<Chromosome*>{}; // silence a warning
}


Main.cpp ++++++++++++++++++++++++++++++++++++++++++++++++



#include "Chromosome.h"
#include "CrossoverStrategies.h" // A bad idea. Forces recompilation when strategies are added

int main()
{
Chromosome * p1 = ChromosomeFactory(/* some construction parameters here */);
p1->m_genes.push_back(0.0); // will fail to compile (incomplete type)
Chromosome * p2 = ChromosomeFactory(/* some construction parameters here */);

// probably should hide the next line with a factory as well
CrossoverStrategy * strategy = new CrossoverStrategyExample1();
strategy->crossover(p1, p2);
}


A quick afterword on security. It always comes at a cost. Generally it makes things harder to use. It makes them harder for an attacker, but it also makes things harder for the legitimate users. Whether it's worth it or not is up to you.






share|improve this answer


























  • Thank you for the suggestions. I'm aware of the fact that I'd need to continually modify Chromosome.h when I add a new strategy. But that is a known limitation and (only) 2 lines of code need to change, so it seems like an acceptable tradeoff for the cleanliness of the code.

    – cridgit
    Jan 3 at 3:33











  • Regarding your option 1 suggestion: the whole purpose is to provide non-const access to Chromosome's private members. If we make the getter non-const then any random class could play with Chromsome's privates. Obviously I want to restrict this access only to CrossoverStrategy and its subclasses. Remember, in effect we're taking what was originally Chromosome::crossover() and externalizing it so we can swap out with different strategies. JavaScript does this beautifully with anonymous functions but I find C++ lamdbas ugly and unreadable by comparison.

    – cridgit
    Jan 3 at 3:37











  • @cridgit What sorts of things can non-CrossoverStrategies do with Chromsome? If the answer is nothing, see if making Chromsome public and applying the pimpl idiom makes sense.

    – user4581301
    Jan 3 at 5:53











  • The code includes an example of what it needs to do with Chromosome privates: "parent1->m_genes[i] = 0.0;" It needs to access and modify this and perhaps other private members. I looked at pimpl but that also appears to completely hide the private members, so I'm not sure how I'd be able to achieve the desired outcome with pimpl, but thank you for the suggestion.

    – cridgit
    Jan 3 at 8:23






  • 1





    I've figured out how to do this using your example above based on the Pimpl pattern and it is exactly what I was looking for. THANK YOU. I used a public ChromosomeImpl variable in the header file. Then I made the ChromosomeImpl members public in the source file so only the CrossoverStrategy subclasses have access to them. Works beautifully!

    – cridgit
    Jan 4 at 12:44














2












2








2







Option 2 should be rejected because it does not scale. You will be continually modifying Chromosome to keep it up to date with new CrossoverStrategies.



Option 1 is a strange idea because it places the getter function for Chromosome's data members outside of Chromosome. I can see some cases where this is an attractive idea, if getGenes is made protected, but I'm not convinced here. Consider instead



Option 1-A



class Chromosome
{
public:
const std::vector<double>& getGenes() const
{
return m_genes;
}
private:
std::vector<double> m_genes;
};


Everyone who can access a Chromosome can access getGenes, but they can't do anything to damage it and Chromosome remains blissfully ignorant of its users.



Option 3: Use The Pimpl Idiom



Short and stupid example with a few flaws to keep the demo short



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



#include <vector>
class Chromosome; // forward declaration only
class CrossoverStrategy
{
public:
virtual ~CrossoverStrategy() = default;
virtual std::vector<Chromosome*> crossover(Chromosome *parent1, Chromosome *parent2) = 0;
};

Chromosome * ChromosomeFactory(/* some construction parameters here */);
// should also provide a declaration of a factory function to provide CrossoverStrategies


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"
class Chromosome
{
public:
std::vector<double> m_genes;

// silence a warning
Chromosome(): m_genes{}
{

}
};

// Because Chromosome is only defined in this file, only this file can use the internals
// of Chromosome. They are public, but the outside world doesn't know that

Chromosome * ChromosomeFactory(/* some construction parameters here */)
{
// Probably makes and returns a pointer to a Chromosome,
// but could pull it from a list, copy construct from a template, etc...
return new Chromosome(/* some construction parameters here */);
}

// should also provide a definition of a factory function to provide CrossoverStrategies


std::vector<Chromosome*> CrossoverStrategyExample1::crossover(Chromosome *parent1,
Chromosome *parent2)
{
for (unsigned long i = 0; i < parent1->m_genes.size(); i++)
parent1->m_genes[i] = 0.0;
return std::vector<Chromosome*>{}; // silence a warning
}


Main.cpp ++++++++++++++++++++++++++++++++++++++++++++++++



#include "Chromosome.h"
#include "CrossoverStrategies.h" // A bad idea. Forces recompilation when strategies are added

int main()
{
Chromosome * p1 = ChromosomeFactory(/* some construction parameters here */);
p1->m_genes.push_back(0.0); // will fail to compile (incomplete type)
Chromosome * p2 = ChromosomeFactory(/* some construction parameters here */);

// probably should hide the next line with a factory as well
CrossoverStrategy * strategy = new CrossoverStrategyExample1();
strategy->crossover(p1, p2);
}


A quick afterword on security. It always comes at a cost. Generally it makes things harder to use. It makes them harder for an attacker, but it also makes things harder for the legitimate users. Whether it's worth it or not is up to you.






share|improve this answer















Option 2 should be rejected because it does not scale. You will be continually modifying Chromosome to keep it up to date with new CrossoverStrategies.



Option 1 is a strange idea because it places the getter function for Chromosome's data members outside of Chromosome. I can see some cases where this is an attractive idea, if getGenes is made protected, but I'm not convinced here. Consider instead



Option 1-A



class Chromosome
{
public:
const std::vector<double>& getGenes() const
{
return m_genes;
}
private:
std::vector<double> m_genes;
};


Everyone who can access a Chromosome can access getGenes, but they can't do anything to damage it and Chromosome remains blissfully ignorant of its users.



Option 3: Use The Pimpl Idiom



Short and stupid example with a few flaws to keep the demo short



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



#include <vector>
class Chromosome; // forward declaration only
class CrossoverStrategy
{
public:
virtual ~CrossoverStrategy() = default;
virtual std::vector<Chromosome*> crossover(Chromosome *parent1, Chromosome *parent2) = 0;
};

Chromosome * ChromosomeFactory(/* some construction parameters here */);
// should also provide a declaration of a factory function to provide CrossoverStrategies


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"
class Chromosome
{
public:
std::vector<double> m_genes;

// silence a warning
Chromosome(): m_genes{}
{

}
};

// Because Chromosome is only defined in this file, only this file can use the internals
// of Chromosome. They are public, but the outside world doesn't know that

Chromosome * ChromosomeFactory(/* some construction parameters here */)
{
// Probably makes and returns a pointer to a Chromosome,
// but could pull it from a list, copy construct from a template, etc...
return new Chromosome(/* some construction parameters here */);
}

// should also provide a definition of a factory function to provide CrossoverStrategies


std::vector<Chromosome*> CrossoverStrategyExample1::crossover(Chromosome *parent1,
Chromosome *parent2)
{
for (unsigned long i = 0; i < parent1->m_genes.size(); i++)
parent1->m_genes[i] = 0.0;
return std::vector<Chromosome*>{}; // silence a warning
}


Main.cpp ++++++++++++++++++++++++++++++++++++++++++++++++



#include "Chromosome.h"
#include "CrossoverStrategies.h" // A bad idea. Forces recompilation when strategies are added

int main()
{
Chromosome * p1 = ChromosomeFactory(/* some construction parameters here */);
p1->m_genes.push_back(0.0); // will fail to compile (incomplete type)
Chromosome * p2 = ChromosomeFactory(/* some construction parameters here */);

// probably should hide the next line with a factory as well
CrossoverStrategy * strategy = new CrossoverStrategyExample1();
strategy->crossover(p1, p2);
}


A quick afterword on security. It always comes at a cost. Generally it makes things harder to use. It makes them harder for an attacker, but it also makes things harder for the legitimate users. Whether it's worth it or not is up to you.







share|improve this answer














share|improve this answer



share|improve this answer








edited Jan 3 at 19:12

























answered Jan 3 at 3:13









user4581301user4581301

20.8k52033




20.8k52033













  • Thank you for the suggestions. I'm aware of the fact that I'd need to continually modify Chromosome.h when I add a new strategy. But that is a known limitation and (only) 2 lines of code need to change, so it seems like an acceptable tradeoff for the cleanliness of the code.

    – cridgit
    Jan 3 at 3:33











  • Regarding your option 1 suggestion: the whole purpose is to provide non-const access to Chromosome's private members. If we make the getter non-const then any random class could play with Chromsome's privates. Obviously I want to restrict this access only to CrossoverStrategy and its subclasses. Remember, in effect we're taking what was originally Chromosome::crossover() and externalizing it so we can swap out with different strategies. JavaScript does this beautifully with anonymous functions but I find C++ lamdbas ugly and unreadable by comparison.

    – cridgit
    Jan 3 at 3:37











  • @cridgit What sorts of things can non-CrossoverStrategies do with Chromsome? If the answer is nothing, see if making Chromsome public and applying the pimpl idiom makes sense.

    – user4581301
    Jan 3 at 5:53











  • The code includes an example of what it needs to do with Chromosome privates: "parent1->m_genes[i] = 0.0;" It needs to access and modify this and perhaps other private members. I looked at pimpl but that also appears to completely hide the private members, so I'm not sure how I'd be able to achieve the desired outcome with pimpl, but thank you for the suggestion.

    – cridgit
    Jan 3 at 8:23






  • 1





    I've figured out how to do this using your example above based on the Pimpl pattern and it is exactly what I was looking for. THANK YOU. I used a public ChromosomeImpl variable in the header file. Then I made the ChromosomeImpl members public in the source file so only the CrossoverStrategy subclasses have access to them. Works beautifully!

    – cridgit
    Jan 4 at 12:44



















  • Thank you for the suggestions. I'm aware of the fact that I'd need to continually modify Chromosome.h when I add a new strategy. But that is a known limitation and (only) 2 lines of code need to change, so it seems like an acceptable tradeoff for the cleanliness of the code.

    – cridgit
    Jan 3 at 3:33











  • Regarding your option 1 suggestion: the whole purpose is to provide non-const access to Chromosome's private members. If we make the getter non-const then any random class could play with Chromsome's privates. Obviously I want to restrict this access only to CrossoverStrategy and its subclasses. Remember, in effect we're taking what was originally Chromosome::crossover() and externalizing it so we can swap out with different strategies. JavaScript does this beautifully with anonymous functions but I find C++ lamdbas ugly and unreadable by comparison.

    – cridgit
    Jan 3 at 3:37











  • @cridgit What sorts of things can non-CrossoverStrategies do with Chromsome? If the answer is nothing, see if making Chromsome public and applying the pimpl idiom makes sense.

    – user4581301
    Jan 3 at 5:53











  • The code includes an example of what it needs to do with Chromosome privates: "parent1->m_genes[i] = 0.0;" It needs to access and modify this and perhaps other private members. I looked at pimpl but that also appears to completely hide the private members, so I'm not sure how I'd be able to achieve the desired outcome with pimpl, but thank you for the suggestion.

    – cridgit
    Jan 3 at 8:23






  • 1





    I've figured out how to do this using your example above based on the Pimpl pattern and it is exactly what I was looking for. THANK YOU. I used a public ChromosomeImpl variable in the header file. Then I made the ChromosomeImpl members public in the source file so only the CrossoverStrategy subclasses have access to them. Works beautifully!

    – cridgit
    Jan 4 at 12:44

















Thank you for the suggestions. I'm aware of the fact that I'd need to continually modify Chromosome.h when I add a new strategy. But that is a known limitation and (only) 2 lines of code need to change, so it seems like an acceptable tradeoff for the cleanliness of the code.

– cridgit
Jan 3 at 3:33





Thank you for the suggestions. I'm aware of the fact that I'd need to continually modify Chromosome.h when I add a new strategy. But that is a known limitation and (only) 2 lines of code need to change, so it seems like an acceptable tradeoff for the cleanliness of the code.

– cridgit
Jan 3 at 3:33













Regarding your option 1 suggestion: the whole purpose is to provide non-const access to Chromosome's private members. If we make the getter non-const then any random class could play with Chromsome's privates. Obviously I want to restrict this access only to CrossoverStrategy and its subclasses. Remember, in effect we're taking what was originally Chromosome::crossover() and externalizing it so we can swap out with different strategies. JavaScript does this beautifully with anonymous functions but I find C++ lamdbas ugly and unreadable by comparison.

– cridgit
Jan 3 at 3:37





Regarding your option 1 suggestion: the whole purpose is to provide non-const access to Chromosome's private members. If we make the getter non-const then any random class could play with Chromsome's privates. Obviously I want to restrict this access only to CrossoverStrategy and its subclasses. Remember, in effect we're taking what was originally Chromosome::crossover() and externalizing it so we can swap out with different strategies. JavaScript does this beautifully with anonymous functions but I find C++ lamdbas ugly and unreadable by comparison.

– cridgit
Jan 3 at 3:37













@cridgit What sorts of things can non-CrossoverStrategies do with Chromsome? If the answer is nothing, see if making Chromsome public and applying the pimpl idiom makes sense.

– user4581301
Jan 3 at 5:53





@cridgit What sorts of things can non-CrossoverStrategies do with Chromsome? If the answer is nothing, see if making Chromsome public and applying the pimpl idiom makes sense.

– user4581301
Jan 3 at 5:53













The code includes an example of what it needs to do with Chromosome privates: "parent1->m_genes[i] = 0.0;" It needs to access and modify this and perhaps other private members. I looked at pimpl but that also appears to completely hide the private members, so I'm not sure how I'd be able to achieve the desired outcome with pimpl, but thank you for the suggestion.

– cridgit
Jan 3 at 8:23





The code includes an example of what it needs to do with Chromosome privates: "parent1->m_genes[i] = 0.0;" It needs to access and modify this and perhaps other private members. I looked at pimpl but that also appears to completely hide the private members, so I'm not sure how I'd be able to achieve the desired outcome with pimpl, but thank you for the suggestion.

– cridgit
Jan 3 at 8:23




1




1





I've figured out how to do this using your example above based on the Pimpl pattern and it is exactly what I was looking for. THANK YOU. I used a public ChromosomeImpl variable in the header file. Then I made the ChromosomeImpl members public in the source file so only the CrossoverStrategy subclasses have access to them. Works beautifully!

– cridgit
Jan 4 at 12:44





I've figured out how to do this using your example above based on the Pimpl pattern and it is exactly what I was looking for. THANK YOU. I used a public ChromosomeImpl variable in the header file. Then I made the ChromosomeImpl members public in the source file so only the CrossoverStrategy subclasses have access to them. Works beautifully!

– cridgit
Jan 4 at 12:44













2














The first, obvious, option is to consider whether the members of Chromosome should or should not be public. Given that you want an arbitrary number of classes to have access to its data, an obvious option is to make that data public.



A second option is for Chromosome to provide a public getter and setter for the affected data, such as;



 class Chromosome
{
public:

std::vector<double> getGenes() const {return m_genes;};
bool setGenes(const std::vector<double> &newgenes)
{
bool is_error = true;
if (IsValid(newgnes))
{
is_error = false;
m_genes = newgenes;
}
return is_error; // return true if change is rejected
};

private:

std::vector<double> m_genes;
};


Then all CrossOverStrategy and its derived classes need to do, given valid pointers to Chromosomes, is request the genes, do whatever is needed, and (when done) provide a new set of genes back to selected Chromosomes.



Encapsulation of Chromosome is preserved by various measures, since the only way to change genes is via a member function of Chromosome i.e. there is no way of changing genes in a chromosome outside control of the Chromosome class. Which allows Chromosome to do any checks it likes, and reject bad genes if desired.



There is no need for any other class or function to be a friend of Chromosome. A key advantage is that it is not necessary to change the Chromosome class whenever a new class is derived from CrossOverStrategy.



The trade-off is that genes are retrieved and changes by copying the complete set (potential performance hit of copying). But it avoids the need to break encapsulation of the Chromosome class by providing, directly or indirectly, a reference to its private members to any other classes.



If copying the complete set of chromosomes is a bad thing, work out some additional member functions of Chromosome that allow the caller to request partial changes (e.g. update particular genes, insert a set of genes into a specified place in the vector of genes, etc). These additional functions need to work on the same principle: all changes of genes within a Chromosome go via member functions of Chromosome, and there is no "back door" mechanism for other code to sneak changes through.



If you really want, you can make the setter and getter private members of Chromosome, and make only the base class CrossOverStrategy a friend. Then all CrossOverStrategy needs to do is provide protected helpers that only call the private helpers of Chromosome.



class CrossoverStrategy
{
public:
virtual std::vector<Chromosome*> crossover(Chromosome *parent1, Chromosome *parent2) = 0;

protected:

std::vector<double> getGenes(Chromosome *instance)
{
return instance ? instance->getGenes() : std::vector<double>();
};

bool setGenes(Chromosome *instance, const std::vector<double> &newgenes)
{
return instance ? instance->setGenes(newgenes)) : true; // true indicates error
};
};


That way, only the classes derived from CrossOverStrategy can access the protected helpers. If the workings of Chromosome change, then the only class that needs to be adapted in this case is the base CrossOverStrategy class - since its derived classes don't (directly) access Chromosome at all.






share|improve this answer


























  • Peter thank you for this solid answer, it seems to be the most "correct" way and certainly not as ugly as the 2 options I suggested. I'll wait to see if there are any other brilliant ideas before accepting this as the answer.

    – cridgit
    Jan 3 at 13:38
















2














The first, obvious, option is to consider whether the members of Chromosome should or should not be public. Given that you want an arbitrary number of classes to have access to its data, an obvious option is to make that data public.



A second option is for Chromosome to provide a public getter and setter for the affected data, such as;



 class Chromosome
{
public:

std::vector<double> getGenes() const {return m_genes;};
bool setGenes(const std::vector<double> &newgenes)
{
bool is_error = true;
if (IsValid(newgnes))
{
is_error = false;
m_genes = newgenes;
}
return is_error; // return true if change is rejected
};

private:

std::vector<double> m_genes;
};


Then all CrossOverStrategy and its derived classes need to do, given valid pointers to Chromosomes, is request the genes, do whatever is needed, and (when done) provide a new set of genes back to selected Chromosomes.



Encapsulation of Chromosome is preserved by various measures, since the only way to change genes is via a member function of Chromosome i.e. there is no way of changing genes in a chromosome outside control of the Chromosome class. Which allows Chromosome to do any checks it likes, and reject bad genes if desired.



There is no need for any other class or function to be a friend of Chromosome. A key advantage is that it is not necessary to change the Chromosome class whenever a new class is derived from CrossOverStrategy.



The trade-off is that genes are retrieved and changes by copying the complete set (potential performance hit of copying). But it avoids the need to break encapsulation of the Chromosome class by providing, directly or indirectly, a reference to its private members to any other classes.



If copying the complete set of chromosomes is a bad thing, work out some additional member functions of Chromosome that allow the caller to request partial changes (e.g. update particular genes, insert a set of genes into a specified place in the vector of genes, etc). These additional functions need to work on the same principle: all changes of genes within a Chromosome go via member functions of Chromosome, and there is no "back door" mechanism for other code to sneak changes through.



If you really want, you can make the setter and getter private members of Chromosome, and make only the base class CrossOverStrategy a friend. Then all CrossOverStrategy needs to do is provide protected helpers that only call the private helpers of Chromosome.



class CrossoverStrategy
{
public:
virtual std::vector<Chromosome*> crossover(Chromosome *parent1, Chromosome *parent2) = 0;

protected:

std::vector<double> getGenes(Chromosome *instance)
{
return instance ? instance->getGenes() : std::vector<double>();
};

bool setGenes(Chromosome *instance, const std::vector<double> &newgenes)
{
return instance ? instance->setGenes(newgenes)) : true; // true indicates error
};
};


That way, only the classes derived from CrossOverStrategy can access the protected helpers. If the workings of Chromosome change, then the only class that needs to be adapted in this case is the base CrossOverStrategy class - since its derived classes don't (directly) access Chromosome at all.






share|improve this answer


























  • Peter thank you for this solid answer, it seems to be the most "correct" way and certainly not as ugly as the 2 options I suggested. I'll wait to see if there are any other brilliant ideas before accepting this as the answer.

    – cridgit
    Jan 3 at 13:38














2












2








2







The first, obvious, option is to consider whether the members of Chromosome should or should not be public. Given that you want an arbitrary number of classes to have access to its data, an obvious option is to make that data public.



A second option is for Chromosome to provide a public getter and setter for the affected data, such as;



 class Chromosome
{
public:

std::vector<double> getGenes() const {return m_genes;};
bool setGenes(const std::vector<double> &newgenes)
{
bool is_error = true;
if (IsValid(newgnes))
{
is_error = false;
m_genes = newgenes;
}
return is_error; // return true if change is rejected
};

private:

std::vector<double> m_genes;
};


Then all CrossOverStrategy and its derived classes need to do, given valid pointers to Chromosomes, is request the genes, do whatever is needed, and (when done) provide a new set of genes back to selected Chromosomes.



Encapsulation of Chromosome is preserved by various measures, since the only way to change genes is via a member function of Chromosome i.e. there is no way of changing genes in a chromosome outside control of the Chromosome class. Which allows Chromosome to do any checks it likes, and reject bad genes if desired.



There is no need for any other class or function to be a friend of Chromosome. A key advantage is that it is not necessary to change the Chromosome class whenever a new class is derived from CrossOverStrategy.



The trade-off is that genes are retrieved and changes by copying the complete set (potential performance hit of copying). But it avoids the need to break encapsulation of the Chromosome class by providing, directly or indirectly, a reference to its private members to any other classes.



If copying the complete set of chromosomes is a bad thing, work out some additional member functions of Chromosome that allow the caller to request partial changes (e.g. update particular genes, insert a set of genes into a specified place in the vector of genes, etc). These additional functions need to work on the same principle: all changes of genes within a Chromosome go via member functions of Chromosome, and there is no "back door" mechanism for other code to sneak changes through.



If you really want, you can make the setter and getter private members of Chromosome, and make only the base class CrossOverStrategy a friend. Then all CrossOverStrategy needs to do is provide protected helpers that only call the private helpers of Chromosome.



class CrossoverStrategy
{
public:
virtual std::vector<Chromosome*> crossover(Chromosome *parent1, Chromosome *parent2) = 0;

protected:

std::vector<double> getGenes(Chromosome *instance)
{
return instance ? instance->getGenes() : std::vector<double>();
};

bool setGenes(Chromosome *instance, const std::vector<double> &newgenes)
{
return instance ? instance->setGenes(newgenes)) : true; // true indicates error
};
};


That way, only the classes derived from CrossOverStrategy can access the protected helpers. If the workings of Chromosome change, then the only class that needs to be adapted in this case is the base CrossOverStrategy class - since its derived classes don't (directly) access Chromosome at all.






share|improve this answer















The first, obvious, option is to consider whether the members of Chromosome should or should not be public. Given that you want an arbitrary number of classes to have access to its data, an obvious option is to make that data public.



A second option is for Chromosome to provide a public getter and setter for the affected data, such as;



 class Chromosome
{
public:

std::vector<double> getGenes() const {return m_genes;};
bool setGenes(const std::vector<double> &newgenes)
{
bool is_error = true;
if (IsValid(newgnes))
{
is_error = false;
m_genes = newgenes;
}
return is_error; // return true if change is rejected
};

private:

std::vector<double> m_genes;
};


Then all CrossOverStrategy and its derived classes need to do, given valid pointers to Chromosomes, is request the genes, do whatever is needed, and (when done) provide a new set of genes back to selected Chromosomes.



Encapsulation of Chromosome is preserved by various measures, since the only way to change genes is via a member function of Chromosome i.e. there is no way of changing genes in a chromosome outside control of the Chromosome class. Which allows Chromosome to do any checks it likes, and reject bad genes if desired.



There is no need for any other class or function to be a friend of Chromosome. A key advantage is that it is not necessary to change the Chromosome class whenever a new class is derived from CrossOverStrategy.



The trade-off is that genes are retrieved and changes by copying the complete set (potential performance hit of copying). But it avoids the need to break encapsulation of the Chromosome class by providing, directly or indirectly, a reference to its private members to any other classes.



If copying the complete set of chromosomes is a bad thing, work out some additional member functions of Chromosome that allow the caller to request partial changes (e.g. update particular genes, insert a set of genes into a specified place in the vector of genes, etc). These additional functions need to work on the same principle: all changes of genes within a Chromosome go via member functions of Chromosome, and there is no "back door" mechanism for other code to sneak changes through.



If you really want, you can make the setter and getter private members of Chromosome, and make only the base class CrossOverStrategy a friend. Then all CrossOverStrategy needs to do is provide protected helpers that only call the private helpers of Chromosome.



class CrossoverStrategy
{
public:
virtual std::vector<Chromosome*> crossover(Chromosome *parent1, Chromosome *parent2) = 0;

protected:

std::vector<double> getGenes(Chromosome *instance)
{
return instance ? instance->getGenes() : std::vector<double>();
};

bool setGenes(Chromosome *instance, const std::vector<double> &newgenes)
{
return instance ? instance->setGenes(newgenes)) : true; // true indicates error
};
};


That way, only the classes derived from CrossOverStrategy can access the protected helpers. If the workings of Chromosome change, then the only class that needs to be adapted in this case is the base CrossOverStrategy class - since its derived classes don't (directly) access Chromosome at all.







share|improve this answer














share|improve this answer



share|improve this answer








edited Jan 3 at 14:26

























answered Jan 3 at 8:26









PeterPeter

27.9k32157




27.9k32157













  • Peter thank you for this solid answer, it seems to be the most "correct" way and certainly not as ugly as the 2 options I suggested. I'll wait to see if there are any other brilliant ideas before accepting this as the answer.

    – cridgit
    Jan 3 at 13:38



















  • Peter thank you for this solid answer, it seems to be the most "correct" way and certainly not as ugly as the 2 options I suggested. I'll wait to see if there are any other brilliant ideas before accepting this as the answer.

    – cridgit
    Jan 3 at 13:38

















Peter thank you for this solid answer, it seems to be the most "correct" way and certainly not as ugly as the 2 options I suggested. I'll wait to see if there are any other brilliant ideas before accepting this as the answer.

– cridgit
Jan 3 at 13:38





Peter thank you for this solid answer, it seems to be the most "correct" way and certainly not as ugly as the 2 options I suggested. I'll wait to see if there are any other brilliant ideas before accepting this as the answer.

– cridgit
Jan 3 at 13:38











0














Your idea is fundamentally flawed.



On one hand, you say you don't want just anyone to be able to mess with the vector of genes.



On the other hand, you want any descendant of CrossoverStrategy to be able to mess with the vector of genes.



But there's a contradiction. "Any descendant" of a class is "just anyone". Any user is able to inherit from any class and do what they want with your vector of genes. They only need to go through a small one-time inconvenience of inheriting from CrossoverStrategy.



This is the reason why friendship in C++ is not inherited. If it were, access control would be useless in presence of friend classes.



Of course you can simulate inheritable friendship by having a protected getter in CrossoverStrategy as one of the answers suggests. But doing that defeats the purpose of access control. It makes the arrays of genes as good as public.






share|improve this answer


























  • While I agree that making private variables public is to be avoided except in specific circumstances, encapsulation is not binary but a spectrum. Being able to access member variables by subclassing a friend is one step better than 100% public access. So I don't agree that it entirely defeats the purpose of access control - the concept has some merit above just making everything public.

    – cridgit
    Jan 4 at 3:21











  • @cridgit I don't see the spectrum. Access is either open to all or closed. There is no half-open or almost closed.

    – n.m.
    Jan 4 at 7:15
















0














Your idea is fundamentally flawed.



On one hand, you say you don't want just anyone to be able to mess with the vector of genes.



On the other hand, you want any descendant of CrossoverStrategy to be able to mess with the vector of genes.



But there's a contradiction. "Any descendant" of a class is "just anyone". Any user is able to inherit from any class and do what they want with your vector of genes. They only need to go through a small one-time inconvenience of inheriting from CrossoverStrategy.



This is the reason why friendship in C++ is not inherited. If it were, access control would be useless in presence of friend classes.



Of course you can simulate inheritable friendship by having a protected getter in CrossoverStrategy as one of the answers suggests. But doing that defeats the purpose of access control. It makes the arrays of genes as good as public.






share|improve this answer


























  • While I agree that making private variables public is to be avoided except in specific circumstances, encapsulation is not binary but a spectrum. Being able to access member variables by subclassing a friend is one step better than 100% public access. So I don't agree that it entirely defeats the purpose of access control - the concept has some merit above just making everything public.

    – cridgit
    Jan 4 at 3:21











  • @cridgit I don't see the spectrum. Access is either open to all or closed. There is no half-open or almost closed.

    – n.m.
    Jan 4 at 7:15














0












0








0







Your idea is fundamentally flawed.



On one hand, you say you don't want just anyone to be able to mess with the vector of genes.



On the other hand, you want any descendant of CrossoverStrategy to be able to mess with the vector of genes.



But there's a contradiction. "Any descendant" of a class is "just anyone". Any user is able to inherit from any class and do what they want with your vector of genes. They only need to go through a small one-time inconvenience of inheriting from CrossoverStrategy.



This is the reason why friendship in C++ is not inherited. If it were, access control would be useless in presence of friend classes.



Of course you can simulate inheritable friendship by having a protected getter in CrossoverStrategy as one of the answers suggests. But doing that defeats the purpose of access control. It makes the arrays of genes as good as public.






share|improve this answer















Your idea is fundamentally flawed.



On one hand, you say you don't want just anyone to be able to mess with the vector of genes.



On the other hand, you want any descendant of CrossoverStrategy to be able to mess with the vector of genes.



But there's a contradiction. "Any descendant" of a class is "just anyone". Any user is able to inherit from any class and do what they want with your vector of genes. They only need to go through a small one-time inconvenience of inheriting from CrossoverStrategy.



This is the reason why friendship in C++ is not inherited. If it were, access control would be useless in presence of friend classes.



Of course you can simulate inheritable friendship by having a protected getter in CrossoverStrategy as one of the answers suggests. But doing that defeats the purpose of access control. It makes the arrays of genes as good as public.







share|improve this answer














share|improve this answer



share|improve this answer








edited Jan 3 at 19:55

























answered Jan 3 at 19:42









n.m.n.m.

73.4k884170




73.4k884170













  • While I agree that making private variables public is to be avoided except in specific circumstances, encapsulation is not binary but a spectrum. Being able to access member variables by subclassing a friend is one step better than 100% public access. So I don't agree that it entirely defeats the purpose of access control - the concept has some merit above just making everything public.

    – cridgit
    Jan 4 at 3:21











  • @cridgit I don't see the spectrum. Access is either open to all or closed. There is no half-open or almost closed.

    – n.m.
    Jan 4 at 7:15



















  • While I agree that making private variables public is to be avoided except in specific circumstances, encapsulation is not binary but a spectrum. Being able to access member variables by subclassing a friend is one step better than 100% public access. So I don't agree that it entirely defeats the purpose of access control - the concept has some merit above just making everything public.

    – cridgit
    Jan 4 at 3:21











  • @cridgit I don't see the spectrum. Access is either open to all or closed. There is no half-open or almost closed.

    – n.m.
    Jan 4 at 7:15

















While I agree that making private variables public is to be avoided except in specific circumstances, encapsulation is not binary but a spectrum. Being able to access member variables by subclassing a friend is one step better than 100% public access. So I don't agree that it entirely defeats the purpose of access control - the concept has some merit above just making everything public.

– cridgit
Jan 4 at 3:21





While I agree that making private variables public is to be avoided except in specific circumstances, encapsulation is not binary but a spectrum. Being able to access member variables by subclassing a friend is one step better than 100% public access. So I don't agree that it entirely defeats the purpose of access control - the concept has some merit above just making everything public.

– cridgit
Jan 4 at 3:21













@cridgit I don't see the spectrum. Access is either open to all or closed. There is no half-open or almost closed.

– n.m.
Jan 4 at 7:15





@cridgit I don't see the spectrum. Access is either open to all or closed. There is no half-open or almost closed.

– n.m.
Jan 4 at 7:15


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54015468%2fc-friend-subclasses-access-private-members-strategy-pattern%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Monofisismo

Angular Downloading a file using contenturl with Basic Authentication

Olmecas