【问题标题】:should I call void constructor to reset object in move semantic?我应该调用 void 构造函数来重置移动语义中的对象吗?
【发布时间】:2020-04-22 19:16:55
【问题描述】:

据我所知,
void 构造函数的目的是将所有元素从 choas 状态重置为正确的新生状态。
在从右值引用中窃取数据后,这个声明对我来说是非常准确的。
但直接调用 void 构造函数似乎是一种不正当用途,并且placement-new 不适用于此。

所以,

  • 我应该调用 void 构造函数来重置移动语义中的对象吗?
  • 我应该像这样写reset()和invoke()吗?
  • void 构造函数应该做什么?
  • 我的字符串实现是否足够好?
#include <cstring>


class STR_imp
{
    char* cstr;

    static size_t getSize(const char* incstr)
    {
        return std::strlen(incstr)+1;
    };
    STR_imp& fillBy(const char* incstr)
    {
        for(int i=0,size=getSize(incstr);i<size;i++)
            this->cstr[i]=incstr[i];
        return *this;
    };
    STR_imp& reset()
    {
        this->cstr=nullptr;
        return *this;
    };
    STR_imp& invoke()
    {
        if(this->cstr!=nullptr)
            delete[] this->cstr;
        return *this;
    };

    public:
        STR_imp():cstr(nullptr)
        {};
        ~STR_imp()
        {this->invoke().reset();};

        STR_imp(const char* const& incstr);//splited for reading
        STR_imp(char*&& incstr):cstr(incstr)
        {
            incstr=nullptr;
        };
        STR_imp(const STR_imp& instr);//splited for reading
        STR_imp(STR_imp&& instr):cstr(instr.cstr)
        {
            instr.reset();
        };
        STR_imp& operator= (const char* const& incstr);//splited for reading
        STR_imp& operator= (char*&& incstr)
        {
            this->invoke();
            this->cstr=incstr;
            incstr=nullptr;
            return *this;
        };
        STR_imp& operator= (const STR_imp& instr);//splited for reading
        STR_imp& operator= (STR_imp&& instr)
        {
            this->invoke();
            this->cstr=instr.cstr;
            instr.reset();
            return *this;
        };
        char* operator() ()
        {
            return this->cstr;
        };
};
STR_imp::STR_imp(const char* const& incstr):cstr(new char[getSize(incstr)])
{
    this->fillBy(incstr);
};
STR_imp::STR_imp(const STR_imp& instr):cstr(new char[getSize(instr.cstr)])
{
    this->fillBy(instr.cstr);
};
STR_imp& STR_imp::operator= (const char* const& incstr)
{
    this->invoke();
    this->cstr=new char[getSize(incstr)];
    this->fillBy(incstr);
    return *this;
};
STR_imp& STR_imp::operator= (const STR_imp& instr)
{
    this->invoke();
    this->cstr=new char[getSize(instr.cstr)];
    this->fillBy(instr.cstr);
    return *this;
};

【问题讨论】:

  • 你过度设计了;不需要 3/4 的代码。目前还不清楚invoke() 做什么甚至应该做什么。 Invokecalluse 的同义词。
  • 什么是“空构造函数”?我从来没有听说过。
  • 通过 const 引用和右值引用传递 char 指针有什么用?一个STR_imp(const char *); 就足够了。此外,除了复制/移动之外,您可能不应该向operator= 添加任何重载。由于非explicit 构造函数,您仍然可以将char 指针分配给您的对象。
  • @AiDSl 好的。所以,是的,“默认构造函数”。
  • 这是一个默认的默认构造函数。我知道。 ???愚蠢的语言。

标签: c++ constructor move-semantics


【解决方案1】:

我只打算在这里解决有关移动语义的问题,因为您一次只能问一个问题。

移动构造函数通常可以让对象处于它想要的任何状态,只要您记录后置条件以便类的用户知道会发生什么。

例如,C++ 标准库中的许多类声明移出对象处于未指定但有效状态。这意味着您不能对对象的状态做出任何假设,但是您可以询问对象的状态是什么,或者您可以手动将其重置为清除状态。 (“正确”的说法是你只能调用没有前置条件的对象的方法。)

我相信选择这条路线是因为实现移动语义的最有效方式是简单地交换目标和源对象的值。通常这很好,因为在许多情况下,源对象要么被立即销毁,要么被手动清除。

您通常这样做的方式是:

  1. 实现一个有效的swap() 方法:

    void STR_imp::swap(STR_imp & other) {
      std::swap(cstr, other.cstr);
    }
    
  2. 在移动分配时,只需交换:

    STR_imp& STR_imp::operator=(STR_imp&& instr) {
      swap(instr);
      return *this;
    }
    
  3. 在移动构造时,构造默认状态然后交换:

    STR_imp::STR_imp(STR_imp&& instr) : STR_imp{} {
      swap(instr);
    }
    

这种模式有很多优点。最大的优势是,如果您的 swap() 方法正常工作,真的很难出错。另一个优点是它强制你支持交换,这在许多不同的场景中都是有用的。

【讨论】:

  • 所以,在这里自我回答(可能是错误的):1,3.我所说的构造函数应该只适用于无效状态的对象,只有在非构造对象的情况下,对象生命周期,它应该始终有效但可以不指定。
【解决方案2】:

C++ 中的析构函数会破坏您的对象,因为它的名称表明在调用它之后,对象的底层内存已被释放。这也意味着您无法再访问它,因为它不存在于您的应用程序中。

如果你想重置一个对象,你可以调用你的reset函数,它会按照你想要的方式清除数据。如果您想要一个干净的对象副本,您可以使用移动赋值运算符,然后在复制的对象上调用 reset

请注意,将cstr 设置为nullptr 不会释放内存。您应该这样做:

~STR_imp() { delete cstr; }

STR_imp& reset()
{
  delete cstr;
  cstr = nullptr;
  return *this;
}

【讨论】:

  • ok,但是因为是char数组,所以应该是delete[] cstr吧?
猜你喜欢
  • 1970-01-01
  • 2013-08-08
  • 1970-01-01
  • 1970-01-01
  • 2012-06-12
  • 2023-04-08
  • 1970-01-01
  • 2010-11-10
  • 2012-06-19
相关资源
最近更新 更多