【问题标题】:Using smart pointers as a class member使用智能指针作为类成员
【发布时间】:2014-10-20 19:49:26
【问题描述】:

我一直在阅读智能指针,最近在课堂上我的助教说我们不应该使用原始指针。现在,我在网上做了很多阅读并查看了这个网站上的不同问题,但我仍然对智能指针的某些方面感到困惑。我的问题是:如果我希望在我的程序中使用它,我会使用哪个智能指针?我会展示一些代码。

所以我有一个基本的应用程序类,它从类 AI 中声明对象。注意:出于测试原因,我有两个不同的智能指针,一个是唯一的,一个是共享的。

// Application class in Application.h

class Application
{
public:
    Application(){}
    ~Application(){}

    //... additional non-important variables and such

    unique_ptr<AI> *u_AI; // AI object using a unique pointer
    shared_ptr<AI> *s_AI; // AI object using a shared pointer

    //... additional non-important variables and such

    void init();
    void update();
};

// AI class in AI.h

class AI
{
public:
    AI(){}
    ~AI(){}

    bool isGoingFirst;
};

在Application init函数中,我想创建AI对象,然后我想在update函数中使用它。我不确定我是否完全正确地声明了我的指针,但我知道它可以编译并且它可以在 init 函数中分配和打印数据。更多代码如下。

void Application::init()
{
    //.. other initialization's.

    std::shared_ptr<AI> temp(new AI());
    sh_AI = &temp;
    sh_AI->isGoingFirst = true;

    //.. other initialization's.
    // Function ends.
}

void Application::update()
{
    if(sh_AI->get()->isGoingFirst == true)
    {
         // Do something
    }
    else
    {
        // Do something else
    }

    // Other code below
}

稍后在我的程序中,调用了更新函数,它使用了我在类 Application 中声明的相同 AI 智能指针。我发现智能指针 AI 对象正在被删除。我知道智能指针具有自动内存管理功能,但是是否有一个智能指针可以让您在不同的功能中使用它而不会产生任何重大问题,例如内存泄漏或悬空引用?还是我错过了智能指针的全部意义?

很抱歉,如果在另一个问题中回答了这个问题,但我阅读了很多其他问题,虽然我对智能指针有更多了解,但我仍在学习。谢谢!

【问题讨论】:

  • unique_ptr&lt;AI&gt; *u_AI 是指向智能指针的指针,这可能不是您想要的。在 init 函数中不需要临时指针,可以直接使用 reset 函数存储结果。
  • 你的代码已经严重损坏了。您应该解决使用原始 dangling 指针的根本问题。我认为您需要先掌握基础知识。
  • 所有规则都有例外,即使是这个...
  • 恕我直言,您不应该一直使用智能指针。当您使用new then 创建对象时,您应该立即 将其放入智能指针中。在许多用例中,您可以安全地使用原始指针,因为知道它将在适当的时间自动删除。只有当您无法控制哪个对象最后使用指针时,您才需要使用shared_ptr。即便如此,只有指针的共享所有者需要在智能指针中保存一份副本。生命周期比拥有对象短的子作用域可以安全地使用原始指针。
  • 智能指针应该用于所有权语义,而不是作为原始指针的替代品。

标签: c++ pointers memory-management smart-pointers


【解决方案1】:

正如 Neil Kirk 在 cmets 中指出的那样,这些声明不是您想要的:

unique_ptr<AI> *u_AI; // AI object using a unique pointer
shared_ptr<AI> *s_AI; // AI object using a shared pointer

u_AI 和 s_AI 仍然是原始指针的对象。重点是消除直接管理原始指针的需要。所以现在你将它们替换为:

unique_ptr<AI> u_AI; // AI object using a unique pointer
shared_ptr<AI> s_AI; // AI object using a shared pointer

要分配您创建的指针,请使用函数 make_unique 或 make_shared:

u_AI = unique_ptr<AI>(new AI()); // Yu may be able to use make_unique like 
                                 // make_shared but it's new to C++14. may not be available
s_AI = make_shared<AI>();

然后,当你需要访问它们时,你只是假装它们是指针,所以在你的更新函数中:

if(sh_AI->get()->isGoingFirst == true)

变成:

if(sh_AI->isGoingFirst == true)

至于何时使用 unique_ptr 与 shared_ptr,您可以通过回答以下问题来回答:当有人复制 Application 时,我希望发生什么?即:

Application app1;
app1.init();
Application app2 = app1; // ?? what happens to AI object in app2?

有 3 个可能的答案:

  1. 我希望 app2 中有一个额外的 AI 副本。在这种情况下,您使用 unique_ptr 并确保您实现了一个复制构造函数来执行复制。
  2. 我希望 app2 和 app1 共享 AI 的副本。在这种情况下,您使用 shared_ptr 并且默认的复制构造函数将为您完成这项工作。
  3. 我不希望有 Application 的副本。(这对于名为 Application 的类很有意义)。在这种情况下,它并不重要(在这种情况下,我将默认为 unique_ptr)并删除复制构造函数:

    应用程序(常量应用程序&)=删除;

【讨论】:

  • 当前标准C++11中没有make_unique
  • @Fozi 哎呀谢谢。当您的编译器支持新功能时,很难将这些事情记在脑海中。
  • 非常感谢您的明确解释。我现在明白了很多!
【解决方案2】:

简短回答:由于您的指针是公开的,我建议您使用shared_ptr。但是,您的指针不需要是公开的,因此如果它是私有的,您可以使用unique_ptr,因为您只在自己的实例中使用它。

事实上,这并不重要(而且我知道我会对此表示反对)。使用unique_ptr有两个原因:

  1. 它永远不会离开你的模块,你只需要一个裸指针的替代品
  2. 您想明确表明它不应该离开您的模块。

另一方面,如果您需要共享指针(即使以只读方式),则必须使用shared_ptr

很多时候使用shared_ptr 开始更方便,但出于上述原因2) 值得使用unique_ptr

不是使用unique_ptr 的理由:性能。我只想说make_shared

现在是你的代码

这是定义智能指针的方式:

std::shared_ptr<AI> s_AI;
std::unique_ptr<AI> u_AI;

这是你初始化它的方式:

s_AI = std::make_shared<AI>(); // or
s_AI = std::shared_ptr<AI>(new AI);

u_AI = std::unique_ptr<AI>(new AI);

请注意,C++11 中没有 std::make_unique。它将在 C++14 中,编写替代品并不难,但事实上在 C++11 中没有。

这是你使用指针的方式:

s_AI->isGoingFirst;

就是这样,它的行为就像一个普通的指针。仅当您必须将其传递给需要指针的函数时,您才需要使用.get()

这是删除(清空)指针的方法:

s_AI.reset();

再次,我建议您将指针设为私有。如果您需要将其传递出去,请确保使用 shared_ptr 并编写一个 getter 方法:

std::shared_ptr<AI> getAI() const {
    return s_AI;
}

请记住,如果您这样做,您不能假设您的 AI 对象会在您的应用程序对象被销毁时被销毁。

【讨论】:

    猜你喜欢
    • 2013-03-16
    • 2011-12-25
    • 2015-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多