2016-09-06 6 views
3

Я нашел проблему при использовании стороннего кода, который не может быть изменен. Мне нужно сделать копию члена объекта. Я не могу этого делать строго, потому что у одного из внутренних членов есть оператор частного назначения. Единственное решение, которое я нашел, сложно, поэтому я хочу спросить вас, видите ли вы какие-либо красные огни, которые могут повлиять на мою программу.Использование оператора размещения и нового оператора вместо оператора присваивания

Вот упрощенный код, который я имею дело с (помните, что я не могу изменить его!):

#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; 
} 

Мое решением является вызов Content деструктора вручную освободить любую динамически выделенную память в содержании и его внутренние классы. Память в стеке остается нетронутой, поэтому я могу ее повторно использовать с оператором размещения-new, который вызывает конструктор копирования, который присутствует, и делает именно то, что мне нужно. Когда основная область функции заканчивается, объект «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

Я не вижу никаких проблем * per se *, просто убедитесь, что конструктор копий остается в курсе, или у вас будут проблемы ....... Я фактически использовал аналогичные стратегии для этого до – DarthRubik

+1

Что происходит, когда вы do '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'. BTW Я только что увидел ошибку и скоро отредактирую свой ответ. – cdonat