I have the following setup:
foo.h
:
class A {
friend class B;
private:
A() {}
};
class B {
public:
void addObject(Object &o); // adds to myMember; A is not exposed!
void computeResult(Result &r); // uses myMember to compute result
private:
vector<A> myMember;
};
An object of A
will never be exposed to any program including foo.h
. The vector with A
's is only there to help B
in its computation adapter role. By making A
's constructor private, I thought I could avoid other compilation units from using it, and it seems to work. However, the problem is in
foo.cpp
void B::computeResult(Result &r) {
MyCustomStorage<A> storage;
A *a = storage.allocate(); // error: "A::A() is private"
}
where part of MyCustomStorage
looks like so:
template <typename T>
class MyCustomStorage {
T *allocate() {
...
T *ptr = new T[count]; // error: "A::A() is private"
...
}
};
But I thought since allocate()
is called from a member function, this wouldn´t happen! How could I solve this?
Making A
a friend to MyCustomStorage
seems very spaghetti-codish. Making A
a private nested class of B
makes all sorts of help-classes in foo.cpp
fail because "A is private".
So what would be the cleanest way to solve this?
SOLUTION
I ended up going with @potatoswatter 's second solution, with these appropriate changes:
foo.h
class B {
public:
void addObject(Object &o); // adds to myMember; A is not exposed!
void computeResult(Result &r); // uses myMember to compute result
private:
class A {
private:
A() {}
};
class Helper; // forward declared!
vector<A> myMember;
};
foo.cpp
class B::Helper {
int help(A& a) { return 42; } // no problem! Helper is a member of B
}
void B::computeResult(Result &r) {
MyCustomStorage<A> storage;
A *a = storage.allocate(); // no problem! A is a member of B
Helper h;
h.help(*a); // no problem!
}
It's not the constructor of A
that is private, it's the entire class.
The best solution is to create a "private" namespace. C++ doesn't have namespace-level access protection, but it's reasonable to expect that users won't access an unfamiliar namespace.
namespace impl {
struct A {
A() {}
};
}
class B {
public:
void addObject(Object &o); // adds to myMember; A is not exposed!
void computeResult(Result &r); // uses myMember to compute result
private:
vector<impl::A> myMember;
};
Another approach is to make A
a member of B
. This offers "real" access protection at the expense of deeper nesting. I personally prefer the first solution, and to avoid nested classes.
class B {
public:
void addObject(Object &o); // adds to myMember; A is not exposed!
void computeResult(Result &r); // uses myMember to compute result
private:
struct A {
A() {}
};
vector<A> myMember;
};
Any helpers that need A
would then need to be friends. There are various workarounds like nesting A
in a base class with protected
access, but really, namespace impl
offers the least compromises.