2011-06-11 91 views
16

我喜歡const成員變量的想法,尤其是當我將C函數包裝到類中時。構造函數需要一個資源句柄(例如文件描述符),該句柄在整個對象生命週期內保持有效,並且析構函數最終關閉它。 (這是背後的想法,對吧?)移動構造函數和const成員變量

但與C++ 0x移動構造函數我遇到了問題。由於析構函數也被稱爲「未加載」的對象,我需要防止清理資源句柄。由於成員變量是const我沒有辦法分配值-1或INVALID_HANDLE(或等價值),以指示析構函數,它不應該做任何事情。

如果一個對象的狀態被移動到另一個對象時,有沒有方法可以調用析構函數?

例子:

class File 
{ 
public: 
    // Kind of "named constructor" or "static factory method" 
    static File open(const char *fileName, const char *modes) 
    { 
     FILE *handle = fopen(fileName, modes); 
     return File(handle); 
    } 

private: 
    FILE * const handle; 

public: 
    File(FILE *handle) : handle(handle) 
    { 
    } 

    ~File() 
    { 
     fclose(handle); 
    } 

    File(File &&other) : handle(other.handle) 
    { 
     // The compiler should not call the destructor of the "other" 
     // object. 
    } 

    File(const File &other) = delete; 
    File &operator =(const File &other) = delete; 
}; 

回答

7

沒有,有沒有辦法做到這一點。我建議如果你真的接受了handle變量爲const,那麼你應該有一個非const標誌成員變量,它指示了銷燬是否應該做任何事情。

+1

你的答案肯定是一種方式。但是與C++ 0x有關,我不喜歡析構函數必須檢查是否應該真正發生解析的樣式。他們是不是應該認爲這個物體已經完全投入運行,現在已經成爲毀滅的一點? – mazatwork 2011-06-11 18:28:16

+1

@mazatwork:好吧,想想這樣。假設你有一個複雜的對象,可能處於幾種不同的狀態,每個狀態都需要一組不同的析構函數。例如,就像有一個緩存可能會或可能不會被初始化,或者可能需要或不需要關閉的數據庫連接。當你沒有關閉未在析構函數中打開的數據庫連接時,你是不是「真的在破壞」?當然不是。這基本上是一回事。你仍然在摧毀,只是對象所在的狀態不能滿足很多工作。 – Omnifarious 2011-06-11 19:00:37

+0

爲什麼不讓移動構造函數做一些清理(如果它實際上是必要的),所以析構函數留下了真正的破壞。在我看來,這會更好。因爲我們談到同一個對象,雙重破壞可能是不合理的。你用複雜對象的例子是我嘗試避免使用像RAII和DI這樣的技術的一個例子。 – mazatwork 2011-06-11 19:56:21

3

實現移動構造函數的典型方法是將被移動實例的成員置零或以其他方式使其失效(有關簡單示例,請參見MSDN)。因此,我認爲在這裏不要使用const,因爲它與移動語義的目標不兼容。

+0

那麼,你實際上可以使用'const',編譯器仍然會生成移動構造函數(當然,如果滿足[所有需求](https://stackoverflow.com/a/8285499))。下面是一個例子作爲證明:[codepad.org/Xh4va2eR](http://codepad.org/Xh4va2eR)(忽略鍵盤舊的編譯器錯誤..) – 2017-09-04 13:59:13

13

這就是爲什麼你不應該聲明所述成員變量constconst成員變量通常不起任何作用。如果你不希望用戶改變FILE*,那麼不要爲它們提供這樣做的功能,並且如果你想阻止你偶然改變它,那麼標記你的函數const。但是,請勿自行創建成員變量const - 因爲那時您在開始使用移動或複製語義時遇到樂趣

+7

Const成員變量是有用的像引用。通常你想保持一個值,你知道你不會改變它,這可能允許編譯器進行一些優化。 只有當我沒有一些可變變量時,纔將這些方法標記爲const。 – mazatwork 2011-06-11 17:53:18

+3

根據我的經驗,它並沒有真正允許任何有用的編譯器優化。它阻止了許多非常有用的語義。 – Puppy 2013-01-14 23:01:09

+0

確實如此!我遇到了** fun **,因爲我將一個基礎類的成員聲明爲一個const成員。 – user8385554 2017-08-18 06:18:28

-1

引用計數是解決您的問題的標準方法。考慮給你的班級添加引用計數;手動或使用現有的工具,如boost shared_ptr。

+2

這是一個正在提出的問題(正在處理「文件」的移動)的正交主題。你提出的建議是'shared_ptr 's。可能是合適的,但可能不合適,而且肯定會涉及更大的設計更改 – 2014-04-02 18:05:18

0

其實我今天也遇到過這個問題。不願意接受「不能做」 &「使用的shared_ptr /引用計數」,谷歌上搜索更多的,我想出了這個基類:

class Resource 
{ 
private: 
    mutable bool m_mine; 

protected: 
    Resource() 
    : m_mine(true) 
    { 
    } 

    Resource(const Resource&)  = delete; 
    void operator=(const Resource&) = delete; 

    Resource(const Resource&& other) 
    : m_mine(other.m_mine) 
    { 
     other.m_mine = false; 
    } 

    bool isMine() const 
    { 
     return m_mine; 
    } 
}; 

所有方法和構造的保護,你需要從繼承它使用它。注意可變字段:這意味着後裔可以是類中的一個常量成員。例如,

class A : protected Resource 
{ 
private: 
    const int m_i; 

public: 
    A() 
    : m_i(0) 
    { 
    } 

    A(const int i) 
    : m_i(i) 
    { 
    } 

    A(const A&& a) 
    : Resource(std::move(a )) 
    , m_i  (std::move(a.m_i)) // this is a move iff member has const move constructor, copy otherwise 
    { 
    } 

    ~A() 
    { 
     if (isMine()) 
     { 
      // Free up resources. Executed only for non-moved objects 
      cout << "A destructed" << endl; 
     } 
    } 
}; 

現在A的字段可以是const。請注意,我已經繼承了protected,所以用戶不會意外地將A轉換爲資源(或者非常樂意去破解它),但是A仍然不是最終的,因此您仍然可以繼承它(從Resource繼承的有效理由是例如具有分開的讀取和讀寫訪問)。這是極少數情況下保護繼承並不意味着您的設計有問題的情況之一;然而,如果你覺得難以理解,你可能只是使用公有繼承。

然後,假設你有一個struct X

struct B 
{ 
    const A m_a; 
    const X m_x; 

    B(const A&& a, const X& x) // implement this way only if X has copy constructor; otherwise do for 'x' like we do for 'a' 
    : m_a(std::move(a)) 
    , m_x(   x ) 
    { 
    } 

    B(const B&& b) 
    : m_a(std::move(b.m_a)) 
    , m_x(std::move(b.m_x)) // this is a move iff X has move constructor, copy otherwise 
    { 
    } 

    ~B() 
    { 
     cout << "B destructed" << endl; 
    } 
}; 

需要注意的是B的領域也可以是常量。我們的移動構造函數是const。鑑於你的類型有適當的移動構造函數,任何堆分配的內存可以在對象之間共享。

相關問題