【问题标题】:Disallow/Redirect C++ delete?禁止/重定向 C++ 删除?
【发布时间】:2014-05-04 03:22:32
【问题描述】:

我有一个模块 (dll/so),它导出一个工厂函数,该函数返回一个然后调用的对象。然后使用模块的接口(纯虚拟)用户可以创建不同的对象。所有对象的创建都是通过接口进行的,因此使用与我的模块关联的运行时而不是应用程序运行时进行。

由于分配是在模块内部进行的,因此删除也需要进行,因为如果应用程序的运行时间与我的模块不同,gpf/segfault 时间。所以我有一个“释放”成员,它执行自删除。

void foo::release(void) 
{
    delete this;
}

一切正常,但它确实需要模块的用户行为。

我的问题是:

  • 是否可以阻止某人直接对我的对象发出删除操作(或将其重定向到从我的模块内存池中删除)?
  • 如果不作为备用计划,是否可以在我的对象中检测到这一点,以便我可以抛出断言来强制执行良好的行为?

例如:

iFoo* foo = createFoo ();
foo->release();           // Allowed and expected
delete foo;               // Disallowed

【问题讨论】:

  • 您是否考虑过“制作私有析构函数”选项?
  • 一个选择不表现的用户可能会在很多方面破坏你。让您的界面易于使用正确,难以使用错误,并且不要太担心恶意意图。
  • 也许返回一个智能指针是为了? en.wikipedia.org/wiki/Smart_pointer
  • @dave,这可以用于接口吗?我的界面是 100% 纯的。客户端应用程序只知道接口,隐藏在模块内的实现标头中的私有“析构函数”是否仍会被捕获?如果是这样,那将完全解决我的担忧。
  • @Chris,声明一个私有析构函数会阻止类被继承,所以它应该被保护。然而,如果你不能阻止子类扩展你的纯接口来实现 public 析构函数,那么这个解决方案可能不适合你的问题。

标签: c++ destructor delete-operator


【解决方案1】:

在 OP 的 cmets 中,@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

【讨论】:

  • 嗨 Lilshieste,感谢演示。我相信这是我需要的解决方案。我很好奇的一个问题。虚拟析构函数是否需要在接口文件中以便编译器拾取错误?我猜它会,因为它怎么会知道。我也没有意识到 SO 允许从同一个人发布多个答案。整洁,谢谢。
  • @Chris 起初,我也不知道我可以发布另一个答案(必须检查 Meta 站点)。你是对的 - 虚拟析构函数需要在接口文件中才能使一切正常工作。
【解决方案2】:

编辑时:这种方法只会让用户更难删除资源 - 它不会完全阻止它。 (我不会删除这个答案,因为它可能仍然有用。)

如果你真的想阻止某人对你的对象调用 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

【讨论】:

    猜你喜欢
    • 2017-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-09
    • 2011-12-04
    • 2018-01-30
    • 2023-03-28
    • 2014-10-08
    相关资源
    最近更新 更多