2016-09-06 30 views
3

使用不能更改的第三方代碼時發現問題。我需要製作一個對象成員的副本。我不能嚴格這樣做,因爲內部成員之一有私人分配操作員。我發現的唯一解決方案很棘手,所以我想問你是否看到任何可能影響我的程序的紅燈。使用placement-new操作符和複製構造函數而不是賦值操作符

這裏是我處理的簡化的代碼(記得我不能改變它!):

#include <iostream> 
#include <algorithm> 

class MBool 
{ 
public: 
    MBool() {}; 
    MBool(const MBool& arg) {} 
private:  
    MBool& operator=(const MBool& arg); 
}; 

class InnerContent { 
private: 
    int* pBuffer; 

public: 
    InnerContent() { 
     pBuffer = new int[20]; 
     std::cout << "InnerContent()" << std::endl; 
    } 

    InnerContent(const InnerContent& otherInnerContent) { 
     pBuffer = new int[20]; 
     std::copy(otherInnerContent.pBuffer, otherInnerContent.pBuffer + 20, pBuffer); 
     std::cout << "InnerContent(const InnerContent&)" << std::endl; 
    } 

    ~InnerContent() { 
     std::cout << "~InnerContent()" << std::endl; 
     delete [] pBuffer; 
     pBuffer = nullptr; 
    } 

    virtual void someVirtualFunction() {} 
}; 

class Content { 
public: 
    InnerContent innerContent; 
    int someNumber; 
    MBool boolVar; 

    Content() { 
     std::cout << "Content()" << std::endl; 
    } 
    ~Content() { 
     std::cout << "~Content()" << std::endl; 
    } 
    Content(const Content& otherContent) : 
     innerContent(otherContent.innerContent), 
     someNumber(otherContent.someNumber), 
     boolVar(otherContent.boolVar) 
    { 
     std::cout << "Content(const Content&)" << std::endl; 
    } 

    virtual void someVirtualFunction() {} 
}; 

class A { 
public: 
    Content content; 

    A() { std::cout << "A()" << std::endl; } 
    ~A() { std::cout << "~A()" << std::endl; } 
}; 

class B { 
public: 
    Content content; 

    B() { std::cout << "B()" << std::endl; } 
    ~B() { std::cout << "~B()" << std::endl; } 
}; 

這裏就是我關於用它做(僅此可以修改代碼和擴展):

void copyContent(Content& contentFrom, Content& contentTo) { 
    contentTo.~Content(); 
    new (&contentTo) Content(contentFrom); 
}; 

int main() { 
    A a; 
    B b; 

    // I wish to do this: 
    //b.content = a.content; 
    // but Content class has no operator= function implemented 
    // also I can't use generated assignment operator function because of MBool::operator= is private 

    // The only work-around I found is this: 

    std::cout << "--- Before copying" << std::endl; 
    copyContent(a.content, b.content); 
    std::cout << "--- After copying" << std::endl; 
} 

我的解決辦法是手動調用內容析構函數,以釋放在內容中的任何動態分配的存儲器並且其內的類。內存堆棧保持不變,所以我可以重複使用放置新運算符調用目前的拷貝構造函數,並且完全符合我的需要。當主要功能範圍結束時,'a'對象被正確清理。

代碼輸出:

InnerContent() 
Content() 
A() 
InnerContent() 
Content() 
B() 
--- Before copying 
~Content() 
~InnerContent() 
InnerContent(const InnerContent&) 
Content(const Content&) 
--- After copying 
~B() 
~Content() 
~InnerContent() 
~A() 
~Content() 
~InnerContent() 

我不想讓我自己的函數,所有副本的領域,因爲這個類可以在新版本的更新,有可能是我不會複製其它領域,很可能沒有人會記得修復它。

問題:您是否認爲這可能導致任何內存泄漏或內存損壞?你有沒有看到我沒有提到的任何問題?

+2

取而代之的只是使用一個智能指針指向'Content'。易於用新的替代它的指針。 –

+2

我沒有看到任何問題*本身*,只是確保複製構造函數保持最新,否則您將遇到問題.......我實際上在此之前使用了類似的策略 – DarthRubik

+1

當你做'copyContent(x,x)'? – Barry

回答

2

基本上這個想法應該起作用。爲了保護自己免於忘記調用析構函數,我認爲,你應該將整個想法包裝在類模板之類的智能指針中。在這個例子中,它實際上並不包裝指針,而是包含內容對象本身。

template <typename ContentType> 
class content_wrapper { 
    private: 
     ContentType content_; 
    public: 
     content_wrapper() : content_ {} {}; 
     content_wrapper(const content_wrapper& other) : 
      content_{other.content_} {}; 

     content_wrapper& operator = (const content_wrapper& other) { 
      content_.~ContentType(); 
      new (&content_) ContentType(other); 
      return *this; 
     } 

     ContentWrapper& operator *() { 
      return content_; 
     } 
     ContentWrapper* operator ->() { 
      return &content_; 
     } 
}; 

現在你可以使用它像:

class A { 
    public: 
     content_wrapper<Content> content; 

     A() { std::cout << "A()" << std::endl; } 
     ~A() { std::cout << "~A()" << std::endl; } 
}; 

class B { 
    public: 
     content_wrapper<Content> content; 

     B() { std::cout << "B()" << std::endl; } 
     ~B() { std::cout << "~B()" << std::endl; } 
}; 

int main() { 
    A a; 
    B b; 

    b.content = a.content; // the wrapper will take care. 

    b.content->someVirtualFunction(); 
} 

易於閱讀,你永遠不能忘記的析構函數調用,只要你想分配一個內容對象。

+0

感謝您的回覆。 A類和B類也在圖書館,所以我不能在他們的成員上使用包裝,但我喜歡這個想法。 –

+1

@SzymonKordyaczny然後,您可能會嘗試將'A'和'B'而不是'Content'包裝起來。順便說一句,我剛看到一個錯誤,並很快就會編輯我的答案。 – cdonat

相關問題