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).
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
|
show 3 more comments
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
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 afriend
were automaticallyfriend
s, 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 aChromosome
, why isn't the datapublic
?
– 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
|
show 3 more comments
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
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
c++ inheritance design-patterns friend
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 afriend
were automaticallyfriend
s, 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 aChromosome
, why isn't the datapublic
?
– 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
|
show 3 more comments
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 afriend
were automaticallyfriend
s, 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 aChromosome
, why isn't the datapublic
?
– 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
friend
were automatically friend
s, 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
friend
were automatically friend
s, 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
|
show 3 more comments
3 Answers
3
active
oldest
votes
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.
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 withChromsome
? If the answer is nothing, see if makingChromsome
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
|
show 2 more comments
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 Chromosome
s, 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.
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
add a comment |
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.
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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
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 withChromsome
? If the answer is nothing, see if makingChromsome
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
|
show 2 more comments
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.
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 withChromsome
? If the answer is nothing, see if makingChromsome
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
|
show 2 more comments
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.
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.
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 withChromsome
? If the answer is nothing, see if makingChromsome
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
|
show 2 more comments
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 withChromsome
? If the answer is nothing, see if makingChromsome
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
|
show 2 more comments
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 Chromosome
s, 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.
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
add a comment |
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 Chromosome
s, 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.
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
add a comment |
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 Chromosome
s, 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.
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 Chromosome
s, 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.
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
add a comment |
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
add a comment |
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.
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
add a comment |
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.
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
add a comment |
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.
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.
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
add a comment |
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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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
friend
were automaticallyfriend
s, 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 datapublic
?– 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