在 pimpl 中传递部分构造的对象
Passing the partially constructed object in pimpl
我有一个 class 设置,我已经转换为使用 pimpl,像这样:
(仅提纲,我知道这不会编译)
struct GAME
{
unique_ptr<GAME_IMPL> _impl;
explicit GAME() : _impl( new GAME_IMPL( *this ) );
IMAGES getImages() { return _impl._images; }
};
struct GAME_IMPL
{
IMAGES _images;
PLAYER _player;
explicit GAME_IMPL( GAME& game ) : _player( game ) { }
};
struct PLAYER
{
SPRITE _sprite;
explicit PLAYER( GAME& game ) { _sprite.load( game.getImages() ); }
};
在我转换成粉刺模式之前,这很好。
_sprite.load( game.getImages() )
可以访问 GAME::_images
因为 GAME::_images
在 GAME::_player
.
之前实例化
但是转换成pimpl后,可以看到_sprite.load( game.getImages() )
会失败——PLAYER
无法访问GAME::_images
,因为GAME::getImages
使用了GAME::_impl
,直到 all of GAME_IMPL
构造完成后才分配。
显然我的例子是人为设计的。但是有什么方法可以让 _impl
指针在 GAME_IMPL
对象仍在构造之前或同时被设置?
我考虑了以下几点:
- 仅传递
PLAYER
必要的元素。这很麻烦,因为每次我调整 PLAYER
的字段时,它都涉及修改 PLAYER::PLAYER
的 API。同样在实践中 PLAYER
的构造函数最终可能会采用 30 个奇数参数。
- 使用两阶段初始化,例如
PLAYER::PLAYER()
、PLAYER::Load()
- 这更加混乱,因为它意味着将引用整个转换为指针。
- 改为传递
PLAYER
GAME_IMPL
- 这失去了首先使用 pimpl 的任何好处。
我想你错过了一些细节,这里是编译的设置。
#include <memory>
struct GAME_IMPL;
struct IMAGES {};
// A 'trick' I often use
// for pimpl you can define an abstract base
// helps you check if public and impl class
// implement the same functions
//
// it also ensures you only have a pointer to an interface
// of your implementation in your public header file
struct GAME_ITF
{
virtual ~GAME_ITF() = default;
virtual IMAGES get_Images() = 0;
protected:
GAME_ITF() = default; // prevent accidental instantiation.
};
// in pimpl, game is just forwarding to game_impl
struct GAME : public GAME_ITF
{
std::unique_ptr<GAME_ITF> _impl;
GAME();
virtual ~GAME() = default;
IMAGES get_Images() override
{
return _impl->get_Images();
}
};
struct SPRITE
{
void load(IMAGES) { /*..*/ }
};
struct PLAYER
{
SPRITE _sprite;
explicit PLAYER(GAME_ITF& game)
{
_sprite.load(game.get_Images());
}
};
struct GAME_IMPL : public GAME_ITF
{
IMAGES _images;
PLAYER _player;
GAME_IMPL() :
_player{*this} // <=== player can refer to the implementation's interface (no impl. details needed either)
{
}
IMAGES get_Images() override
{
return IMAGES{};
}
};
GAME::GAME() :
_impl(std::make_unique<GAME_IMPL>())
{
}
int main()
{
GAME game;
IMAGES images = game.get_Images();
}
P Kramer 的 answer 很好地回答了我的问题。
但是,如果有人通过 Google 单独搜索标题 “通过部分构建的 object”,我确实发现了这个是可能的。本质上,嵌套的 class(本例中为 GAME_IMPL
)必须在 parent class(本例中为 GAME
)中分配自己的指针,即
explicit GAME_IMPL( GAME& game ) : _player( (game._impl.reset( this ), game) ) { }
现在 GAME::_impl
在 PLAYER::PLAYER
期间分配,可以使用 GAME::getImages()
。
这对于 pimpl 案例来说很麻烦,但可能会解决类似的问题。
我有一个 class 设置,我已经转换为使用 pimpl,像这样:
(仅提纲,我知道这不会编译)
struct GAME
{
unique_ptr<GAME_IMPL> _impl;
explicit GAME() : _impl( new GAME_IMPL( *this ) );
IMAGES getImages() { return _impl._images; }
};
struct GAME_IMPL
{
IMAGES _images;
PLAYER _player;
explicit GAME_IMPL( GAME& game ) : _player( game ) { }
};
struct PLAYER
{
SPRITE _sprite;
explicit PLAYER( GAME& game ) { _sprite.load( game.getImages() ); }
};
在我转换成粉刺模式之前,这很好。
_sprite.load( game.getImages() )
可以访问 GAME::_images
因为 GAME::_images
在 GAME::_player
.
但是转换成pimpl后,可以看到_sprite.load( game.getImages() )
会失败——PLAYER
无法访问GAME::_images
,因为GAME::getImages
使用了GAME::_impl
,直到 all of GAME_IMPL
构造完成后才分配。
显然我的例子是人为设计的。但是有什么方法可以让 _impl
指针在 GAME_IMPL
对象仍在构造之前或同时被设置?
我考虑了以下几点:
- 仅传递
PLAYER
必要的元素。这很麻烦,因为每次我调整PLAYER
的字段时,它都涉及修改PLAYER::PLAYER
的 API。同样在实践中PLAYER
的构造函数最终可能会采用 30 个奇数参数。 - 使用两阶段初始化,例如
PLAYER::PLAYER()
、PLAYER::Load()
- 这更加混乱,因为它意味着将引用整个转换为指针。 - 改为传递
PLAYER
GAME_IMPL
- 这失去了首先使用 pimpl 的任何好处。
我想你错过了一些细节,这里是编译的设置。
#include <memory>
struct GAME_IMPL;
struct IMAGES {};
// A 'trick' I often use
// for pimpl you can define an abstract base
// helps you check if public and impl class
// implement the same functions
//
// it also ensures you only have a pointer to an interface
// of your implementation in your public header file
struct GAME_ITF
{
virtual ~GAME_ITF() = default;
virtual IMAGES get_Images() = 0;
protected:
GAME_ITF() = default; // prevent accidental instantiation.
};
// in pimpl, game is just forwarding to game_impl
struct GAME : public GAME_ITF
{
std::unique_ptr<GAME_ITF> _impl;
GAME();
virtual ~GAME() = default;
IMAGES get_Images() override
{
return _impl->get_Images();
}
};
struct SPRITE
{
void load(IMAGES) { /*..*/ }
};
struct PLAYER
{
SPRITE _sprite;
explicit PLAYER(GAME_ITF& game)
{
_sprite.load(game.get_Images());
}
};
struct GAME_IMPL : public GAME_ITF
{
IMAGES _images;
PLAYER _player;
GAME_IMPL() :
_player{*this} // <=== player can refer to the implementation's interface (no impl. details needed either)
{
}
IMAGES get_Images() override
{
return IMAGES{};
}
};
GAME::GAME() :
_impl(std::make_unique<GAME_IMPL>())
{
}
int main()
{
GAME game;
IMAGES images = game.get_Images();
}
P Kramer 的 answer 很好地回答了我的问题。
但是,如果有人通过 Google 单独搜索标题 “通过部分构建的 object”,我确实发现了这个是可能的。本质上,嵌套的 class(本例中为 GAME_IMPL
)必须在 parent class(本例中为 GAME
)中分配自己的指针,即
explicit GAME_IMPL( GAME& game ) : _player( (game._impl.reset( this ), game) ) { }
现在 GAME::_impl
在 PLAYER::PLAYER
期间分配,可以使用 GAME::getImages()
。
这对于 pimpl 案例来说很麻烦,但可能会解决类似的问题。