3

我有以下设置:

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. 带有 's 的向量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失败,因为“A 是私有的”。

那么解决这个问题的最干净的方法是什么?

解决方案

我最终选择了@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!
}
4

2 回答 2

3

这不是私有的构造函数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提供的妥协最少。

于 2015-08-11T13:50:07.423 回答
1

恕我直言,你有几个选择。您可以 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;
};

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

于 2015-08-11T13:57:48.753 回答