C++ C++;:Can';我不知道如何正确隐藏实现细节

C++ C++;:Can';我不知道如何正确隐藏实现细节,c++,private,encapsulation,access-protection,C++,Private,Encapsulation,Access Protection,我有以下设置: 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: ve

我有以下设置:

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;
};
其中,
MyCustomStorage
的一部分如下所示:

template <typename T>
class MyCustomStorage {
    T *allocate() {
        ...
        T *ptr = new T[count]; // error: "A::A() is private"
        ...
    }
};
foo.cpp

void B::computeResult(Result &r) {
    MyCustomStorage<A> storage;
    A *a = storage.allocate(); // error: "A::A() is private"
}
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!
}
class B::Helper{
int help(A&A){return 42;}//没问题!Helper是B的成员
}
无效B::计算机结果(结果与r){
MyCustomStorage;
A*A=storage.allocate();//没问题!A是B的成员
助手h;
h、 帮助(*a);//没问题!
}

私有的不是
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
的助手都需要成为朋友。在具有
受保护
访问权限的基类中有多种解决方法,如嵌套
A
,但实际上,
名称空间impl
提供了最少的折衷。

IMHO,您有两种选择。您可以1)使用习惯用法,或者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);
}

但是这个习惯用法与您的需求有根本的不同,它限制您在对象
myMember
中使用指针,而您可能不想这样做。内联定义
class A*
也是一种非标准的前向声明性方法。当然,使用智能指针可以减少在此位置发生内存泄漏的可能性。

请参阅使
B
成为
a
的朋友似乎很好。
vector
在功能上不同于
vector
,它是一种容易发生内存泄漏的反模式<代码>矢量
不完整的
A级
将在实践中发挥作用,并计划进行标准化,但它尚未得到正式批准。@Potatoswatter我注意到
矢量
是根本不同的。我将对非标准远期申报方法提出警告。谢谢你的评论。他可以对std::unique_ptr使用部分专门化,以使用pimpl习惯用法并避免原始指针可能出现的内存泄漏。我更喜欢第二个。它客观上更好,因为它封装了
A
,而第一个版本没有。我不希望人们不访问不熟悉的名称空间,
namespace-std
曾经是不熟悉的,但仍然每个人都在使用它。@nwp但是像
std::\uu 1
std:\uu function
这样的名称空间是不熟悉的。大型、流行的库(包括所有标准库实现、Boost等)倾向于使用第一种方法,否则不会为了访问保护而扭曲类。@Potatosatter我使用了第二种解决方案,在我的第一篇文章中编辑。我很想听听你对是否有提到的扭曲问题的评论。@bombax当然,这是有效的。如果说有什么“扭曲”,那就是
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:
    class Impl;
    Impl *pimpl;
};
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);
}
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;
};