简体   繁体   English

使用placement-new运算符和复制构造函数而不是赋值运算符

[英]Use of placement-new operator and copy constructor instead of assignment operator

I found a problem while using 3rd party code which cannot be altered. 我在使用无法更改的第三方代码时发现了问题。 I need to make a copy of object member. 我需要制作一个对象成员的副本。 I can't do this strictly because one of inner members has private assignment operator. 我不能严格执行此操作,因为内部成员之一具有私有赋值运算符。 The only solution I found is tricky so I want to ask you if you see any red lights that can affect my program. 我发现的唯一解决方案很棘手所以我想问你是否看到任何可能影响我程序的红灯。

Here's the simplified code I'm dealing with (remember that I cannot change it !): 这是我正在处理的简化代码(请记住, 我无法改变它 !):

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

And here's what I'm about to do with it (only this code can be modified and extended): 这就是我将要做的事情(只有这段代码可以修改和扩展):

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

My solution is to call Content destructor manually to free any dynamically allocated memory in Content and its inner classes. 我的解决方案是手动调用Content析构函数以释放Content及其内部类中的任何动态分配的内存。 Memory on the stack remains untouched so I can reuse it with placement-new operator that calls copy constructor that is present and does exactly what I need. 堆栈上的内存保持不变,因此我可以将其重新使用placement-new操作符,该操作符调用存在的复制构造函数并完全按照我的需要执行操作。 When main function scope ends 'a' object is cleaned up properly. 当主要功能范围结束时,'a'对象被正确清理。

Code output: 代码输出:

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

I don't want to make my own function that copies all the fields because this class can be updated in new version and there may be additional field that I will not copy and most probably no one will remember to fix it. 我不想创建自己的复制所有字段的函数,因为这个类可以在新版本中更新,并且可能还有其他字段我不会复制,很可能没有人会记得修复它。

Question: Do you think this may cause any memory leaks or memory corruption? 问题:您认为这可能会导致内存泄漏或内存损坏吗? Do you see any problems that I didn't mention? 你看到我没有提到的任何问题吗?

Basically the Idea should work. 基本上,这个想法应该有效。 To protect yourself from forgetting to call the destructor, I think, you should wrap the whole think in a kind of smart pointer like class template. 为了保护自己不要忘记调用析构函数,我认为,你应该把整个思想包装成类模板这样的智能指针。 In this example it actually does not wrap a pointer, but the content object itself. 在这个例子中,它实际上不包装指针,而是包装内容对象本身。

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

now you can use it like that: 现在你可以像这样使用它:

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();
}

Easy to read and you can never forget the destructor call, whenever you want to assign a content object. 易于阅读,只要您想分配内容对象,就永远不会忘记析构函数调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM