请原谅我,如果已经提出这个问题,我没有找到任何对我具体问题的答案。
我在库中创建了一个类,我希望某些类能够创建和销毁,其他类可以访问其他公共函数。拥有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; }
或者,如果DeletableA
或B.h
中需要A.h
类(由于内联,模板或希望在{{1}中拥有所有class A
相关类它可以在构造函数上使用“传递键”移动到那里,因此没有其他类可以创建一个。即使析构函数将被暴露,也不会有其他类会被A.h
删除。
显然,这个解决方案要求DeletableA
知道class B
的实例(如果它没有在Deletable A
中公开,那么通常会生成类)并且仅存储{{1}通过公共函数公开的,但是,它是建议的最灵活的设置。
虽然其他一些类仍然可以创建A.h
的子类(因为A*
不是“final”),你可以在A的构造函数中添加另一个“pass key”来防止如果你愿意,这种行为。
答案 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:您可能会更乐意重载new
和delete
并限制对它们的访问权限,而不是限制对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 delete
和operator 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
之外没有其他客户端可以使用构造函数,因此无论如何都不需要使用析构函数。
您可以通过声明副本和移动构造函数以及new
和delete
运算符来进一步限制谁可以访问析构函数。
<强>更新强>
使析构函数公开并声明副本并移动构造函数似乎解决了所有问题。您甚至不需要声明new
和delete
运算符或其数组变体。
这是我认为应该满足您的大部分需求。
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;
};
它可以具有以下成员数据类型:
A
类型的对象。A
类型的对象的原始指针。shared_ptr<A>
类型的对象。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;
};