У меня есть следующие настройки:
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;
};
Объект A
никогда не будет подвергаться какой-либо программе, включая foo.h
, Вектор с A
только там, чтобы помочь B
в роли адаптера вычислений. Делая A
Это частный конструктор, я думал, что смогу избежать его использования другими модулями компиляции, и это, похоже, работает. Однако проблема заключается в
foo.cpp
void B::computeResult(Result &r) {
MyCustomStorage<A> storage;
A *a = storage.allocate(); // error: "A::A() is private"}
где часть MyCustomStorage
выглядит так:
template <typename T>
class MyCustomStorage {
T *allocate() {
...
T *ptr = new T[count]; // error: "A::A() is private"...
}
};
Но я так и думал allocate()
вызывается из функции-члена, этого не произойдет! Как я мог решить это?
Изготовление A
друг MyCustomStorage
кажется очень спагетти-треска. Изготовление A
частный вложенный класс B
делает все виды справочных классов в foo.cpp
потерпеть неудачу, потому что «А является частным».
Итак, что будет самым чистым способом решить эту проблему?
РЕШЕНИЕ
В конечном итоге я выбрал второе решение @potatoswatter с соответствующими изменениями:
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!
}
Это не конструктор A
это личное, это весь класс.
Лучшее решение — создать «личное» пространство имен. C ++ не имеет защиты доступа на уровне пространства имен, но разумно ожидать, что пользователи не получат доступ к незнакомому пространству имен.
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;
};
Другой подход заключается в том, чтобы сделать A
член B
, Это обеспечивает «настоящую» защиту доступа за счет более глубокого вложения. Я лично предпочитаю первое решение и избегать вложенных занятий.
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;
};
Любые помощники, которые нуждаются A
тогда нужно будет дружить. Существуют различные обходные пути, такие как вложение A
в базовом классе с protected
доступ, но на самом деле, namespace impl
предлагает наименьшее количество компромиссов.
ИМХО, у вас есть пара вариантов. Вы можете либо 1) Использовать Pimpl идиома, или, 2) вы можете использовать предварительную декларацию.
Пример идиома Pimpl:
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 Impl;
Impl *pimpl;
};
И в вашем * .cpp файле вы можете определить Impl
класс и использовать его мужество.
class B::Impl {
public:
std::vector<A> stuff;
}
B::B() : pimpl(new Impl) {
}
B::~B() {
delete pimpl;
}
void B::AddObject(Object &o) {
pimpl->stuff.Fx(o);
}
Вы также можете использовать умный указатель для идиомы Pimpl, я просто не здесь для ясности / краткости.
Форвардная декларация также может быть использована, если A
находится в том же пространстве имен, что и B
class B {
public:
void addObject(Object &o); // adds to myMember; A is not exposed!
void computeResult(Result &r); // uses myMember to compute result
private:
std::vector<class A*> myMember;
};
Но эта идиома принципиально отличается от ваших требований и ограничивает вас использованием указателя внутри вашего объекта myMember
что вы, возможно, не хотите делать. Встроенное определение class A*
это также нестандартный форвардно-декларативный подход. Конечно, использование интеллектуальных указателей уменьшит вероятность утечек памяти в этом месте.