限制对C ++构造函数和析构函数的访问

时间:2014-09-18 21:05:23

标签: c++ constructor destructor

请原谅我,如果已经提出这个问题,我没有找到任何对我具体问题的答案。

我在库中创建了一个类,我希望某些类能够创建和销毁,其他类可以访问其他公共函数。拥有friend class不是我想要的,因为朋友类将访问我不想要的成员变量和成员函数。我偶然发现this idiom几乎可以工作,除了析构函数,因为它不能采取额外的参数。有了这个成语,我得到了:

class B;
class A
{
    public:
        class LifecycleKey
        {
            private:
                LifecycleKey() {}
                friend class B;
        };

        A(LifecycleKey); // Now only class B can call this
        // Other public functions

    private:
        ~A(); // But how can I get class B to have access to this?

        void somePrivateFunction();

        // Members and other private functions
};

如上面的代码所示,该解决方案不允许class B访问析构函数。

虽然上述任何一个问题都不是违规行为,因为我总是可以公开并且只是说“RTFM”。

我的问题是:

是否有某种方法可以限制对特定类(但只有ctor和dtor)的ctor和dtor访问,同时遵循更为人熟知的语法(如果人们需要,将内容放在堆栈中,通过删除销毁等等) )?

非常感谢任何帮助!

A.h

中的

class B;
class A
{
    protected:
        A() {}
        virtual ~A() {}
        A(const A&); // Implement if needed
        A(A&&); // Implement if needed

    public:
        // Public functions

    private:
        void somePrivateFunction();

        // Members and other private functions
};
B.h

中的

class B
{
    public:
        B();
        ~B();
        const A* getA() const;

    private:
        A* m_a;
}
B.cpp

中的

namespace {
    class DeletableA : public A {
        public:
            DeletableA() : A() {}
            DeletableA(const DeletableA&); // Implement if needed
            DeletableA(DeletableA&&); // Implement if needed
            ~DeletableA() {}
    }
}

#include B.h
B::B() : m_a(new DeletableA()) {}
B::~B() { delete static_cast<DeletableA*>(m_a); }
const A* B::getA() const { return m_a; }

或者,如果DeletableAB.h中需要A.h类(由于内联,模板或希望在{{1}中拥有所有class A相关类它可以在构造函数上使用“传递键”移动到那里,因此没有其他类可以创建一个。即使析构函数将被暴露,也不会有其他类会被A.h删除。

显然,这个解决方案要求DeletableA知道class B的实例(如果它没有在Deletable A中公开,那么通常会生成类)并且仅存储{{1}通过公共函数公开的,但是,它是建议的最灵活的设置。

虽然其他一些类仍然可以创建A.h的子类(因为A*不是“final”),你可以在A的构造函数中添加另一个“pass key”来防止如果你愿意,这种行为。

4 个答案:

答案 0 :(得分:1)

使用mediator-class:

class mediator;
class A
{
/* Only for B via mediator */
    A();
    ~A(); // But how can I get class B to have access to this?
    friend class mediator;
/* Past this line the official interface */
public:
    void somePrivateFunction();
protected:
private:
};

class B;
class mediator
{
    static A* createA() { return new A{}; }
    static void destroyA(const A* p) { delete p; }
    // Add additional creators and such here
    friend class B;
};

因此,只有作为B接口一部分的中介才能获得完全访问权限。

BTW:您可能会更乐意重载newdelete并限制对它们的访问权限,而不是限制对dtor的访问权限。
优点:如果直接初始化变量而不复制,则通常可以在堆栈上进行分配。

void* operator new(std::size_t);
void* operator new[](std::size_t);
void operator delete(void*);
void operator delete[](void*);
void operator delete(void*, std::size_t) noexcept;
void operator delete[](void*, std::size_t) noexcept;

答案 1 :(得分:1)

为了实现类B应该是唯一一个能够实例化和销毁类A的对象的目标:

  • 对于静态和自动变量,只需要限制对构造函数的访问,并且您已经这样做了。

  • 对于动态分配的对象,您可以限制对其释放函数operator deleteoperator delete[]的访问,并将析构函数保留为公共。这禁止除B之外的其他代码删除对象。

  • 对于动态对象,您可以从具有A虚拟析构函数或命名自毁函数的接口派生类protected,该函数具有类B作为朋友。然后,B可以通过转换到它有权访问的接口来销毁任何动态A对象。

显式调用析构函数的代码应该得到任何东西。

请记住,您永远不会构建对恶意代码的坚不可摧的防御,您只是建立一个合理的检测和编译时间报告无意中的错误使用。

答案 2 :(得分:0)

使用shared_ptr

class K{
public:
    int x;
private:
    ~K(){}; 
     K(){};  
private:
    friend class K_Creater;
    friend class K_Deleter; 
};

struct K_Deleter{  void operator()(K* p) { delete p; }  };

struct K_Creater{ 
    static shared_ptr<K> Create(){ 
        return shared_ptr<K>(new K,  K_Deleter() ); 
    }
};



//K* p = new K;    prohibited
shared_ptr<K> p = K_Creator::Create();

另一个答案是:

#include <iostream>

class A
{
public:

    class Key
    {
    private:
        Key(){
            std::cout << "Key()" << std::endl;
        }
        ~Key(){
            std::cout << "~Key()" << std::endl;
        }
        friend class B;
    };

    A(Key key){
        std::cout << "A(Key key)" << std::endl;
    }
    void seti(){ i_=0;}
private:
    int i_;
};

class B{
public:
    static void foo(){

        A a{A::Key()};

        A* pa = new A( A::Key() );
        delete pa;

        static A sa({});

    }
};

int main(){

    B::foo();

    //A a{A::Key()};  prohibit
    //A* pa = new A( A::Key() );   prohibit
    //delete pa;   prohibit
    //static A sa({});   prohibit

    return 0;
}

答案 3 :(得分:0)

我的看法:

任何有权访问构造函数的类/函数都应该有权访问析构函数。

由于~A()是公开的,因此您应该公开A()。由于除B之外没有其他客户端可以使用构造函数,因此无论如何都不需要使用析构函数。

您可以通过声明副本和移动构造函数以及newdelete运算符来进一步限制谁可以访问析构函数。

<强>更新

使析构函数公开并声明副本并移动构造函数似乎解决了所有问题。您甚至不需要声明newdelete运算符或其数组变体。

这是我认为应该满足您的大部分需求。

class B;

class PassKey
{
   private:
      PassKey() {}
      ~PassKey() {}
   friend class B;
};

class A
{
   public:
      A(PassKey) {}
      ~A() {}

   private:
      // Declare away
      A(A const&);
      A(A&&);
};

现在,我们来看看B可以拥有的内容:

class B
{
   public:
      B() : a(PassKey()), ap(new A(PassKey())), ap2(new A(PassKey())) {}
      ~B() { delete ap; }

      A const& getA() const {return a;}

      A a;
      A* ap;
      std::shared_ptr<A> ap2;
};

它可以具有以下成员数据类型:

  1. A类型的对象。
  2. 指向A类型的对象的原始指针。
  3. shared_ptr<A>类型的对象。
  4. B的成员函数也可以创建任何上述类型的对象。

    其他类不能使用A类型的对象,因为它们无法以任何方式构造它。以下所有以各种形式使用A的尝试均失败。

    struct C
    {
       C() : a(PassKey()) {} // Can't construct an instance of A 
                             // since C doesn't have access to
                             // PassKey's constructor.
       A a;
    };
    
    struct D
    {
       D() : a(new A(PassKey())) {} // Can't construct an instance of A 
                                    // since D doesn't have access to
                                    // PassKey's constructor.
       A* a;
    };
    
    struct E
    {
       E(A const& a) : ap(new A(a)) {}  // Can't construct an instance of A 
                                        // since E doesn't have access to
                                        // A's copy constructor.
       A* ap;
    };
    
    class F
    {
       public:
          F(A& a) : ap(new A(std::move(a))) {} // Can't construct an instance of A 
                                               // since F doesn't have access to
                                               // A's move constructor.
          A* ap;
    };