管理指向派生对象的指针集合的最佳方式

Best way to manage a collection of pointers to derived objects

本文关键字:指针 集合 最佳 方式 对象 派生 管理      更新时间:2023-10-16

我是来自 c# 的 c++ 新手。以下代码不起作用,我不确定它需要什么。任何帮助我了解它为什么不起作用以及它应该进行哪些适当更改的见解将不胜感激。

// GamePlayScreen derives from ScreenBase
std::unique_ptr<GameplayScreen> m_game(new GameplayScreen());
// MenuScreen derives from ScreenBase
std::unique_ptr<MenuScreen> m_menu(new MenuScreen());
// PauseScreen derives from ScreenBase
std::unique_ptr<PauseScreen> m_pause(new PauseScreen());        
std::vector<std::unique_ptr<ScreenBase>*> screens;
screens.push_back(&m_game); // this gets the error

我收到编译错误:

C2284"无法将参数 1 从 'std::unique_ptr<_Ty> *' 转换为 'std::unique_ptr<_Ty> *&&'

如果我注释掉最后一行,它可以很好地编译。

基本上,我希望有一个派生项的集合(或者更确切地说是指向它们的指针)。我已经尝试了各种"指向和/或引用"参数的方法以及建立 T 的各种方法vector<T>但解决方案让我无法找到。

我读了很多:

。你可能想要shared_ptrs...

我鼓励人们在不确定时默认unique_ptr。 在没有内存所有权设计的情况下降低shared_ptr正是导致循环内存泄漏的原因。

是的,您也可以使用 unique_ptr 创建循环内存泄漏。 然而,我的经验是,当使用unique_ptr时,它会鼓励设计人员了解谁拥有什么,随着设计的发展,使循环内存泄漏的可能性降低,并且在发生时更容易调试。

如果在这个设计过程中,设计师发现共享所有权语义实际上是需要的,那么一定要达到shared_ptr(可能weak_ptr打破这些循环)。

最后,尽快将原始指针绑定到智能指针。 以下是遵循最佳实践的OP问题的可编译草图:

#include <type_traits>
#include <utility>
#include <memory>
#include <vector>
// A general purpose factory function for unique_ptrs
// Feel free to make this factory function more specific to your domain
template <class T, class ...Args>
typename std::enable_if
<
    !std::is_array<T>::value,
    std::unique_ptr<T>
>::type
make_ScreenParts(Args&& ...args)
{
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
// Your class hierarchy
struct ScreenBase
{
    // Don't forget to make your destructors virtual
    virtual ~ScreenBase() = default;
};
// Future-proof your code with typedef's
// If / when you need to switch smart pointer types
//  or container types, you'll thank yourself
typedef std::unique_ptr<ScreenBase> ScreenPtr;
typedef std::vector<ScreenPtr> ScreenContainer;
struct GameplayScreen
    : public ScreenBase
{
};
struct MenuScreen
    : public ScreenBase
{
};
struct PauseScreen
    : public ScreenBase
{
};
int main()
{
    // raw pointers never exposed here...
    ScreenContainer screens;
    screens.push_back(make_ScreenParts<GameplayScreen>());
}

这里没有要验证的编译器,但我相信你可以:

// GamePlayScreen derives from ScreenBase
std::shared_ptr<GameplayScreen> m_game(new GameplayScreen());
// MenuScreen derives from ScreenBase
std::shared_ptr<MenuScreen> m_menu(new MenuScreen());
// PauseScreen derives from ScreenBase
std::shared_ptr<PauseScreen> m_pause(new PauseScreen());        
std::vector<std::shared_ptr<ScreenBase>> screens;
screens.push_back( m_game );

就像@cheers和hth-alf说的那样,你可能想要共享指针。

尝试更改最后两行,如下所示:

std::vector<std::unique_ptr<ScreenBase>> screens;
screens.push_back(std::move(m_game));

我删除了星号,因为您可能想要一个unique_ptr向量,而不是指向unique_ptr的指针向量。此外,顾名思义,unique_ptr意味着一次只能有一个unique_ptr拥有所有权,因此您需要明确std::move它才能转让所有权。

使用 shared_ptr 而不是 unique_ptr ,因为您将从许多位置引用这些屏幕对象。

听起来您

有这样一种情况,即您想管理具有特定类型的各种屏幕,但也要将另一个屏幕列表作为基本类型,以便您可以在所有屏幕上执行某些操作,而不必知道特定类型屏幕的详细信息。

我认为在这种情况下,您只需要一个原始指针:

// GamePlayScreen derives from ScreenBase
std::unique_ptr<GameplayScreen> m_game(new GameplayScreen());
// MenuScreen derives from ScreenBase
std::unique_ptr<MenuScreen> m_menu(new MenuScreen());
// PauseScreen derives from ScreenBase
std::unique_ptr<PauseScreen> m_pause(new PauseScreen());        
std::vector<ScreenBase*> screens;
screens.push_back(&*m_game);

您只需要注意,如果您破坏屏幕,不要让屏幕中的指针悬空。