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());
}

この例をVisualStudio2005 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でこれを試してみます。

何が起こっているのかわかりません。デストラクタが定義されている時点(つまり、でBar.cpp)で、の定義Bar::Implが利用可能であるため、問題はないはずです。なぜこれが機能し、機能しFooないのBarですか?

ここで何が欠けていますか?

4

2 に答える 2

5

これは、deleterboost::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 に答える