【问题标题】:Tree structure with incomplete types and unique_ptr具有不完整类型和 unique_ptr 的树结构
【发布时间】:2016-10-13 21:06:39
【问题描述】:

我有一个关于不完整类型和 unique_ptr 的快速问题。 我试图有一个简单的树状结构,这里已经简化了,我得到了一些关于不完整类型的编译器错误,我知道我必须定义生成的 dtor。虽然这并没有解决问题,但至少在我正在测试它的 msvc 中。

我不得不做的是,对于每个需要销毁不完整类型的类,我只需要在 .cpp 文件中包含相关的头文件。明确定义 dtor 并没有帮助,这让我感到惊讶。

Level1.h

#include "Level2Vector.h"

class Level1
{
public:
    Level1() : lvl2_vec(this) {}

private:
    Level2Vector lvl2_vec;
};

Level1.cpp

#include "Level2.h" // this was needed to not get the incomplete type
#include "Level3.h" // this was needed to not get the incomplete type 

Level2.h

#include "Level3Vector.h"

class Level1;

class Level2
{
public:
    Level2(Level1* const lvl1) : parent_(lvl1), lvl3_vec(this){}

private:
    Level1* parent_;
    Level3Vector lvl3_vec;
};

Level2.cpp

#include "Level2.h"
#include "Level3.h"  // this was needed to not get the incomplete type

Level3.h

class Level2;

class Level3
{
public:
    Level3(Level2* const lvl2) : parent_(lvl2) {}

private:
    Level2* parent_;
};

Level2Vector.h

class Level1;
class Level2;

class Level2Vector : public std::vector<std::unique_ptr<Level2>>
{
public:
    Level2Vector(Level1* lvl1) : parent_(lvl1) {}

private:
    Level1* parent_;
};

Level3Vector.h

class Level2;
class Level3;

class Level3Vector : public std::vector<std::unique_ptr<Level3>>

{
public:
    Level3Vector(Level2* lvl2) : parent_(lvl2) {}
    //~Level3Vector();

private:
    Level2* parent_;
};

我错过了什么吗? 是否每个可能使用 Level2Vector 的类都需要包含 Level2.h 标头?

【问题讨论】:

  • 继承自std::vector :-(
  • 我可以通过组合拥有自己的向量类,或者私下从向量继承。重点不在于它,所以我不想使问题复杂化。我知道公共继承和标准容器......与问题相关的任何内容?

标签: c++ c++11


【解决方案1】:

析构函数的类型应该是完整的,但是析构函数是内联自动生成的,这意味着每个销毁类的地方都应该有std::unique_ptr拥有的类的定义。

避免这个问题的简单方法是为每个具有std::unique_ptr 成员的类声明析构函数。所以:

class Level3Vector
{
public:
    explicit Level3Vector(Level2* lvl2) : parent_(lvl2) {}
    ~Level3Vector();

    // And so rule of 5
    Level3Vector(const Level3Vector&) = delete;
    Level3Vector& operator =(const Level3Vector&) = delete;

    Level3Vector(Level3Vector&&) = default;
    Level3Vector& operator =(Level3Vector&&) = default;

private:
    std::vector<std::unique_ptr<Level3>> lvl3s;
    Level2* parent; // or std::observer_ptr<Level2> parent;
};

在 cpp 中:

#include <Level3.h>

Level3Vector::~Level3Vector() = default;

那么在其他类中销毁Level3Vector不需要&lt;Level3.h&gt;的include。

【讨论】:

    【解决方案2】:

    一般来说,您可以使用前向声明的类型(比如在头文件中使用该类型定义指针);但是类型的定义必须在需要定义类型之前可用。例如。在 Level1.h 中,您定义了 Level2Vector 类型的对象(不是指向 Level2Vector 的指针!);所以 Level1.h 必须包含 Level2Vector.h。

    考虑到这一点,让我们检查您的问题“是否每个可能使用 Level2Vector 的类都需要包含 Level2.h 标头?”答案是不。 Level2.h 提供了Level2 类的定义。所以正确的看待方式是:一个文件(是否使用Level2Vector)需要包含Level2.h,如果该文件需要Level2的定义。

    【讨论】:

    • 问题是为什么没有它编译会失败?如果我使用原始指针,它编译得很好,不需要任何包含。我理解为什么以及是什么,但是 unique_ptr 有一些不同。头文件仅在一个空的 cpp 实现文件中,这无关紧要,但没有它将无法编译
    • 很难准确了解您的问题。看看“Effective Modern C++”的第 22 条。在线提供 PDF 版本。总结包括:对于std::unique_ptr pImpl指针,在类头声明特殊成员函数,但在实现文件中实现。即使默认功能实现是可接受的,也要这样做。上述建议适用于 std::unique_ptr,但不适用于 std::shared_ptr。
    • @DonghuiZhang:事实上Level2Vector的析构函数需要定义Level2,而析构函数是inline,任何使用和销毁Level2Vector的文件都需要定义@987654325 @,即使它没有明确使用它。
    • @Jarod42 我也尝试过提供显式析构函数,但这并没有帮助。我会看一下 Scott Meyers 的书,但我认为这就是它所说的。它几乎有一个默认的 dtor。将再次检查。
    猜你喜欢
    • 2016-05-05
    • 1970-01-01
    • 2019-03-02
    • 2016-09-10
    • 2022-01-09
    • 1970-01-01
    • 2016-12-29
    • 2018-06-04
    • 2012-05-12
    相关资源
    最近更新 更多