QScopedPointer,boost :: scoped_ptr - 为什么抱怨不完整的类型?

时间:2014-09-12 09:43:36

标签: c++ c boost qt4 smart-pointers

我有c-Structure,我想嵌入一个cpp类而不会中毒我的全局命名空间,所以我不想包含c-header。

这就是为什么我要使用具有前向声明结构名称的智能范围指针(QScopedPointerboost::scoped_ptr)。

我不明白的是在编译时失败的两个提到的作用域指针的实现:

升压:

  

错误C2027:使用未定义的类型' xxx'

template<class T> inline void checked_delete(T * x)
{
    // intentionally complex - simplification causes regressions
    typedef char type_must_be_complete[ sizeof(T)? 1: -1 ]; // < here
    (void) sizeof(type_must_be_complete);
    delete x;
}

和Qt中的相同:

  

错误C2027:使用未定义的类型&#39; xxx&#39;

template <typename T>
struct QScopedPointerDeleter
{
    static inline void cleanup(T *pointer)
    {
        // Enforce a complete type.
        // If you get a compile error here, read the section on forward declared
        // classes in the QScopedPointer documentation.
        typedef char IsIncompleteType[ sizeof(T) ? 1 : -1 ]; // < here
        (void) sizeof(IsIncompleteType);

        delete pointer;
    }
};

引用的文档对我没有帮助。它表示前向声明的类的析构函数不必是内联的,并且必须在每个可能的范围内清除指定指针时都可用。但我的c结构没有析构函数。

所以我有两个问题:

  1. 为什么要这样检查?因为知道调用delete的大小似乎无关紧要。
  2. 如何处理?

3 个答案:

答案 0 :(得分:9)

  1. 智能指针中保存的对象类型必须在智能指针被破坏的位置知道,因此可以调用保留对象的正确析构函数
  2. 你的类(持有智能指针的那个)有析构函数吗?如果不是 - 在cpp文件中添加一个。否则编译器会在看到您的类定义时尝试添加一个,但由于智能指针的析构函数尝试访问未知类型,因此无法执行此操作。

答案 1 :(得分:6)

  

我的c-Structure没有析构函数。

首先,。你的struct实际上有一个析构函数 - implicitly-declared destructor


无论如何,让我们继续吧。

delete pointer;

编译此代码时,我们应该调用*pointer的析构函数。但是,如果*pointer是不完整类型,我们就无法知道要调用的析构函数。在这种情况下,标准[expr.delete]表示它会导致未定义的行为

  

如果被删除的对象在删除时具有不完整的类类型,并且完整的类具有非平凡的析构函数或释放函数,则行为是未定义的。

正如您所看到的,如果您的结构没有非平凡的析构函数或释放函数(特定于类operator delete),那么它不是UB。但是,您可能可以在结构中添加析构函数 - 执行。如果你没有解决这一点,它就变成了错误的bug。 (编译器不必报告它;它只是UB,而不是非法代码。)所以被认为是一种好习惯。

因此,删除不完整的类型确实是我们应该避免的。为了避免这种情况,我们使用了这个技巧。

typedef char type_must_be_complete[ sizeof(T)? 1: -1 ]; // < here
(void) sizeof(type_must_be_complete);

如果sizeof(T)是不完整类型,T是非法代码,因此在程序因UB而疯狂之前,它可以减少编译时错误。

我强烈建议你尽管加快速度,但要包括它;虽然你的结构很简单,并且没有operator delete,但是可以在不修复的情况下添加它们,这会导致UB。

答案 2 :(得分:0)

也许此链接将帮助您找出答案: http://www.bnikolic.co.uk/blog/cpp-checked-delete.html

由于“ boost / checked_delete.hpp” 标头,

scoped_ptr 需要使用完全定义的类型作为其模板参数。

就这些事情而言,我个人更喜欢 std :: unique_ptr (例如,当 Init()方法用于一些构造是私有的),但boost没有唯一的指针。

有时,如果方便的话,您可以使用 boost :: shared_ptr ,因为它不表示这些错误。