5

当使用带有 pImpl 习惯用法的智能指针时,如

struct Foo
{
private:
    struct Impl; 
    boost::scoped_ptr<Impl> pImpl;
};

明显的问题是在析构函数的生成Foo::Impl点是不完整的。Foo

编译器通常会在那里发出警告,并且boost::checked_deleteBoost 智能指针在内部使用它,静态断言该类Foo::Impl是完整的,如果不是这种情况则触发错误。

为了编译上面的示例,因此必须编写

struct Foo
{
    ~Foo();

private:
    struct Impl; 
    boost::scoped_ptr<Impl> pImpl;
};

Foo::~Foo并在实现文件中实现一个空,这里Foo::Impl是完整的。这是智能指针相对于裸指针的优势,因为我们不能不实现析构函数。

到目前为止,一切都很好。但是当我尝试在类似的Bar类中引入模板构造函数时遇到了一个奇怪的行为(完整代码,请自行尝试):

// File Bar.h
#ifndef BAR_H
#define BAR_H 1

#include <vector>
#include <boost/scoped_ptr.hpp>

struct Bar
{
    template <typename I>
    Bar(I begin, I end);

    ~Bar();

private:
    struct Impl;
    boost::scoped_ptr<Impl> pImpl;

    void buildImpl(std::vector<double>&);
};


template <typename I>
Bar::Bar(I begin, I end)
{
    std::vector<double> tmp(begin, end);
    this->buildImpl(tmp);
}

#endif // BAR_H

// File Bar.cpp
#include "Bar.h"

struct Bar::Impl
{
    std::vector<double> v;
};

void Bar::buildImpl(std::vector<double>& v)
{
    pImpl.reset(new Impl);
    pImpl->v.swap(v);
}

Bar::~Bar() {}

// File Foo.h
#ifndef FOO_H
#define FOO_H 1

#include <boost/scoped_ptr.hpp>


struct Foo
{
    Foo();
    ~Foo();

private:
    struct Impl;
    boost::scoped_ptr<Impl> pImpl;
};

#endif // FOO_H

// File Foo.cpp
#include "Foo.h"

struct Foo::Impl
{};


Foo::Foo() : pImpl(new Impl)
{}


Foo::~Foo() {}


// File Main.cpp
#include "Foo.h"
#include "Bar.h"

int main()
{
    std::vector<double> v(42);
    Foo f;
    Bar b(v.begin(), v.end());
}

使用 Visual Studio 2005 SP1 编译此示例时,出现以下错误Bar但未出现错误Foo

1>Compiling...
1>main.cpp
1>c:\users\boost_1_45_0\boost\checked_delete.hpp(32) : error C2027: use of undefined type 'Bar::Impl'
1>        c:\users\visual studio 2005\projects\checkeddeletetest\checkeddeletetest\bar.h(15) : see declaration of 'Bar::Impl'
1>        c:\users\boost_1_45_0\boost\smart_ptr\scoped_ptr.hpp(80) : see reference to function template instantiation 'void boost::checked_delete<T>(T *)' being compiled
1>        with
1>        [
1>            T=Bar::Impl
1>        ]
1>        c:\users\boost_1_45_0\boost\smart_ptr\scoped_ptr.hpp(76) : while compiling class template member function 'boost::scoped_ptr<T>::~scoped_ptr(void)'
1>        with
1>        [
1>            T=Bar::Impl
1>        ]
1>        c:\users\visual studio 2005\projects\checkeddeletetest\checkeddeletetest\bar.h(16) : see reference to class template instantiation 'boost::scoped_ptr<T>' being compiled
1>        with
1>        [
1>            T=Bar::Impl
1>        ]
1>c:\users\boost_1_45_0\boost\checked_delete.hpp(32) : error C2118: negative subscript
1>c:\users\boost_1_45_0\boost\checked_delete.hpp(34) : warning C4150: deletion of pointer to incomplete type 'Bar::Impl'; no destructor called
1>        c:\users\visual studio 2005\projects\checkeddeletetest\checkeddeletetest\bar.h(15) : see declaration of 'Bar::Impl'

我一回到家就用最近的 gcc 试试这个。

我不明白发生了什么:在定义析构函数的地方(即 in Bar.cpp),定义 ofBar::Impl是可用的,所以应该没有任何问题。为什么这适用于Foo而不适用于Bar

我在这里想念什么?

4

2 回答 2

5

boost::shared_ptr<>这是使用删除器时要求对象完整的析构boost::checked_deleter<>函数。因为您将范围构造函数放在头文件中,所以如果您的构造函数抛出,编译器必须生成破坏已构造成员的代码,因此它在实例化此模板构造函数时Bar::Bar(I begin, I end)尝试实例化。boost::scoped_ptr<T>::~scoped_ptr(void)

将智能指针与 pimpl 一起使用并没有多大用处。由于无论如何您通常都需要提供析构函数,因此您也可以放入delete pimpl该析构函数并完成它。

于 2011-02-07T13:19:56.590 回答
1

来自 Boost Boost 文档

请注意, scoped_ptr 要求 T 在销毁时是一个完整类型,但 shared_ptr 不需要。

切换到 shared_ptr 并且一切都应该很好 - 也不需要有析构函数(空或其他)。如果您想使类不可复制,使其具有您从 scoped_ptr 获得的语义,请(私下)从 boost::noncopyable 继承。

于 2012-06-11T12:36:23.887 回答