【问题标题】:C++ Avoid Temporary Object Destruction using Copy ConstructorC++ 使用复制构造函数避免临时对象破坏
【发布时间】:2021-07-07 17:38:06
【问题描述】:

我有一个自定义类型的 Tree 的 std::vector,并且我有一个循环将所有 Tree 推回向量。问题是:我需要推回 temporary Tree 对象,但同时,我必须避免它们被释放(我想将 rvalue 引用构造函数 添加到 Tree 类可以解决问题,但它没有)。

树类:

class Tree
{
    public:
        Tree();
        Tree(Coords, Pixel, Pixel, uint8_t);
        Tree(const Tree&);
        Tree(const Tree&&);
        ~Tree();

        Tree& operator=(const Tree&);
        Tree& operator=(const Tree&&);

        void draw();
        void chop(uint8_t);

        Coords getCoords() const {return log->coords;}

        const Log& getLog() const
        { 
            return *log;
        }

        const Crown& getCrown() const
        {
            return *crown;
        }

    private:
        Log*     log;
        Crown* crown;
};

Tree::Tree():
    log(nullptr), crown(nullptr)
{

}

Tree::Tree(Coords c, Pixel wd, Pixel lvs, uint8_t h):
    log(new Log(c, wd, h)), crown(new Crown({c.x, static_cast<coordsCounter>(c.y + h)}, lvs, getRandBetweenEqu(3, 4)))
{
    draw();
}

Tree::Tree(const Tree& tree):
    log(new Log(tree.log->coords, tree.log->texture, tree.log->height)),
    crown(new Crown(tree.crown->coords, tree.crown->texture, tree.crown->width))
{
    draw();
}

Tree::Tree(const Tree&& tree):
    log(new Log(tree.log->coords, tree.log->texture, tree.log->height)),
    crown(new Crown(tree.crown->coords, tree.crown->texture, tree.crown->width))
{
    draw();
}

Tree& Tree::operator=(const Tree& tree)
{
    if(log != nullptr) delete log;
    if(crown != nullptr) delete crown;

    log   = new Log(*tree.log);
    crown = new Crown(*tree.crown);

    return *this;
}

Tree& Tree::operator=(const Tree&& tree)
{
    if(log != nullptr) delete log;
    if(crown != nullptr) delete crown;

    log   = new Log(*tree.log);
    crown = new Crown(*tree.crown);

    return *this;
}

void Tree::draw()
{
    log->draw();
    crown->draw();
}

Tree::~Tree()
{
    chop(0);
}

void Tree::chop(uint8_t h)
{
    if(crown != nullptr)
    {
        delete crown;
        crown = nullptr;
    }

    if(h == 0)
    {
        if(log != nullptr)
        {
            delete log;
            log = nullptr;
        }
    } else
    {
        if(h <= log->height)
        {
            log->chop(h);
        }
    }
}

函数推回树:

void growTrees(uint8_t m, uint8_t M)
{
    Coords treeCoords;

    for(coordsCounter i = 5; i < grid.getMaxXY().x - 4; i += getRandBetweenEqu(m, M))
    {
        if((treeCoords = getFirstBlock({i, static_cast<coordsCounter>(grid.getMaxXY().y - 1)}, 's', grassTexture) + Coords{0, 1}) != Coords{0, 1})
        {
            trees.push_back(Tree(treeCoords, woodTexture, leavesTexture, getRandBetweenEqu(minTreeHeight, maxTreeHeight)));
        }
    }
}

【问题讨论】:

  • 如果你构造它们,你需要解构它们,即使移动了......
  • 真正的问题是什么?你的班级关注rule of 3/5/0吗?
  • 如果您可以包含Trees 类的代码,则响应可能会更好。在现代 C++ 中,您通常可以通过不定义构造函数或赋值运算符来获得最佳结果。你也可以放置而不是推到向量上。
  • @DarkoNaito_09 -- 如果确定一个对象是可复制的,那么复制的含义必须具有逻辑意义。例如,C++ 中的流类是不可复制的。制作文件流的副本意味着什么?正在写入或读取两个文件?这就是为什么流是不可复制的,因为它没有意义。但是,流类是可移动的,也许你应该放弃复制并让类只是可移动的。
  • 另外,请注意,复制是 编译器 可以做的事情,而您实际上不需要做任何明确的事情。因此,现在您将在整个程序运行过程中获得副本,并且您几乎无法控制它。这就是为什么复制必须简单、高效且没有(可见)副作用的原因。

标签: c++ constructor initialization rvalue-reference temporary-objects


【解决方案1】:

您的移动构造函数/赋值运算符实际上是在复制对象,它们应该采用非 const 右值引用并将移动的对象设置为某个空值。

Tree::Tree(Tree&& tree):
    log(std::exchange(tree.log, nullptr)),
    crown(std::exchange(tree.crown, nullptr))
{
    draw();
}
Tree& Tree::operator=(Tree&& tree)
{
    std::swap(tree.log, log);
    std::swap(tree.crown, crown);
    return *this;
}

【讨论】:

  • 实际上,您可以将复制赋值和移动赋值运算符组合到一个实现中:Tree&amp; Tree::operator=(Tree tree) { std::swap(tree.log, log); std::swap(tree.crown, crown); return *this; } 这允许调用者决定是复制构造还是移动构造 @987654323 @ 参数根据需要。
猜你喜欢
  • 1970-01-01
  • 2011-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-05
  • 2010-12-22
  • 2016-10-10
  • 1970-01-01
相关资源
最近更新 更多