我有一个模块(dll / so),它导出一个工厂函数,该函数返回一个随后被调用的对象。然后,使用模块的接口(纯虚拟)用户可以创建不同的对象。所有对象都是通过接口创建的,因此使用与我的模块关联的运行时而不是应用程序运行时来进行。
由于分配在模块内部进行,因此删除也需要,因为如果应用程序对我的模块有不同的运行时间,则为gpf / segfault时间。所以我有一个“发布”成员,执行自我删除。
void foo::release(void)
{
delete this;
}
一切正常,但确实要求模块的用户行事。
我的问题是:
E.G:
iFoo* foo = createFoo ();
foo->release(); // Allowed and expected
delete foo; // Disallowed
答案 0 :(得分:1)
在对OP的评论中,@ dave建议将纯接口中的析构函数声明为protected
而不是public
。这将彻底阻止外部代码(即实现类外部)调用delete
。
例如:
class IFoo
{
protected:
virtual ~IFoo() { }
public:
virtual void release() = 0;
};
class Foo : public IFoo
{
public:
void release() override
{
delete this;
}
};
IFoo* createFoo()
{
return new Foo();
}
int main()
{
auto foo = createFoo();
foo->release(); // Expected
delete foo; // Cannot access protected destructor of IFoo
Return 0;
}
由于您的工厂函数仅公开纯接口,因此如果实现类恰好提供公共析构函数,则此方法不会中断。如果Foo
声明了公共析构函数,则main
中仍会出现编译器错误,因为main
不知道它实际上正在处理Foo
。
答案 1 :(得分:0)
On Edit:这种方法只会让用户更难以删除资源 - 它并不能完全阻止它。 (我不会删除这个答案,因为它可能仍然有用。)
如果你真的想让某人不要在你的对象上调用delete,那么让他们这样做是违法的 - 从你的工厂函数返回一个值类型。
值类型可以是实际对象周围的瘦包装器,可以提供指针语义,智能指针。
一个粗略的例子:
class IFoo
{
public:
virtual ~IFoo() { }
virtual void release() = 0;
};
class Foo : public IFoo
{
public:
Foo() { }
void release() override
{
delete this;
}
};
// Value type with pointer semantics
template <class T>
class Undeletable
{
private:
T* m_resource;
public:
Undeletable(T* resource)
: m_resource(resource)
{
}
T* operator->()
{
return m_resource;
}
};
// Old factory function
IFoo* createFoo()
{
return new Foo();
}
// New factory function
Undeletable<IFoo> createSafeFoo()
{
return Undeletable<IFoo>(createFoo());
}
int main()
{
auto foo = createFoo();
foo->release(); // Expected
delete foo; // Possible but DO NOT WANT
auto safeFoo = createSafeFoo();
safeFoo->release(); // Expected
delete safeFoo; // Compiler says NOPE
return 0;
}
不幸的是,这只会模糊用户仍然可以删除资源的事实。例如:
delete safeFoo.operator->(); // Deletes the resource