2014-03-26 24 views
1

我有一個模塊(dll/so),它導出一個工廠函數,它返回一個對象然後調用。然後使用模塊的接口(純虛擬)用戶可以創建不同的對象。所有對象的創建都是通過接口進行的,因此使用與我的模塊相關的運行時而不是應用程序運行時進行。禁止/重定向C++刪除?

由於分配發生在模塊內,所以刪除也需要,因爲如果應用程序與我的模塊有不同的運行時間,gpf/segfault時間。所以我有一個「釋放」成員,執行自我刪除。

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

一切工作正常,但它確實需要該模塊的用戶的行爲。

我的問題是:

  • 是否有可能阻止別人發出直接刪除我的對象(或重定向到從我的模塊內存池刪除)?
  • 如果不是作爲一個備份計劃,是否有可能在我的對象中檢測到這一點,所以我可以拋出一個斷言來強制良好的行爲?

E.G:

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

您是否考慮過「製作私有析構函數」選項? – dave

+1

選擇不行爲的用戶可以以許多方式打破你。讓您的界面易於使用,並且很難使用錯誤,不要太擔心惡意的意圖。 –

+0

也許返回一個智能指針是爲了? http://en.wikipedia.org/wiki/Smart_pointer – amdn

回答

1

在評論到OP,@戴夫提出的建議在純接口來聲明析構函數爲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

+0

嗨Lilshieste,演示的thx。我相信這是我需要的解決方案。我很好奇的一個問題。虛擬析構函數是否需要在編譯器的接口文件中找出錯誤?我猜這將是因爲它會如何知道。我也沒有意識到允許從同一個人發佈多個答案。整潔,thx。 – Chris

+0

@Chris起初,我不知道我可以發佈另一個答案(必須檢查Meta網站)。你是正確的 - 虛擬析構函數需要在接口文件中才能正常工作。 – Lilshieste

0

上編輯:這種方法只能使得它更難以讓用戶刪除該資源 - 它不完全阻止它。 (我會避免刪除這個答案,因爲它可能仍然有用。)

如果你真的想讓某人不要在你的對象上調用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