我应该删除"new"中传递给"shared_ptr"函数的指针吗



在以下代码示例中:

#include <iostream>
class Foo{
};
class Bar{
public:
    void addFoo(Foo *foo){
        auto my_foo = std::shared_ptr<Foo>(foo);
    }
};
int main() {
    auto bar = Bar();
    bar.addFoo(new Foo());
    return 0;
}

我需要清理bar.addFoo(new Foo)调用在main()中创建的指针吗?还是由创建它的shared_ptrBar来处理?我的理解是,auto my_foo = std::shared_ptr<Foo>(foo);将使用复制构造函数将此指针复制到my_foo中,使原始指针悬空,对吗?

构造函数获取原始指针的想法就是将所有权传递给std::shared_ptr。所以,不,您不必delete—一个传递给std::shared_ptr的原始指针。这样做将导致双重删除,即UB。

请注意,通常传递原始指针是危险的。考虑以下更通用的示例:

void addFoo(Foo *foo){
        // some code which could throw an exception
        auto my_foo = std::shared_ptr<Foo>(foo);
    }

如果在构造my_foo之前抛出异常,则foo将泄漏。

如果您没有特殊理由传递原始指针,请考虑以下替代方案:

class Bar {
public:
    template<class... Args>
    void addFoo(Args... args){
        auto my_foo = std::make_shared<Foo>(args...);
    }  
};
int main() {
    auto bar = Bar();
    bar.addFoo();
    return 0;
}

在这里,您可以在addFoo()中传递参数(如果有(来构造Foo,而不是在调用addFoo()之前构造Foo

如果需要,可以使用args...的完美转发:

    template<class... Args>
    void addFoo(Args&&... args){
        auto my_foo = std::make_shared<Foo>(std::forward<Args>(args)...);
    }

您编写的代码是正确的。但在现代C++中,不应该使用原始指针newdelete,除非必须与这样的代码进行互操作。如果你能帮上忙(如果问题评论有任何指示,你也可以(,请全程使用智能指针:

#include <iostream>
#include <memory>
class Foo {};
class Bar {
public:
    void addFoo(std::unique_ptr<Foo> foo) {
        auto my_foo = std::shared_ptr<Foo>(std::move(foo));
    }
};
int main() {
    auto bar = Bar();
    bar.addFoo(std::make_unique<Foo>());
    return 0;
}

上面,addFoo成员函数接收指针作为unique_ptr,并使用std::move将指针的所有权从unique_ptr转移到shared_ptr,而不复制引用;在构造shared_ptr之后,使unique_ptr处于空闲状态。您也可以让addFoo直接接收shared_ptr,或者在成员函数内部构造对象,如Evg的答案所示。

使用unique_ptr而不是原始指针可以清楚地表明该方法打算获得分配的所有权,并鼓励调用方自己使用智能指针,这样他们以后就不太可能忘记delete分配。

原始指针不管理寿命终止,但共享指针管理寿命终止。当您从原始指针创建共享指针时,共享指针将获得对象的所有权。这意味着,当指向该对象的最后一个共享指针超出范围时,该对象将被销毁。

在您的代码中,my_foo拥有使用new Foo()创建的对象的所有权,在addFoo返回时超出范围,并且由于它包含唯一的共享引用,因此正确地销毁了该对象。

正确的c++方法如下:

#include <iostream>
class Foo{
};
class Bar{
public:
    void addFoo(Foo foo){
        auto my_foo = std::make_shared<Foo>(foo);
    }
    
};
int main() {
    auto bar = Bar();
    bar.addFoo(Foo());
    return 0;
}

这避免了任何原始指针或裸新指针,并且是完全例外安全的。此外,std::make_shared还引入了一些性能优势。这里有一件令人困惑的事情是,代码似乎不必要地复制了Foo对象,然而,由于C++17,由于Return Value Optimization(RVO(,您可以保证根本没有副本(当将Foo作为参数传递给addFoo时(。

您可以使用make_shared创建共享指针。如果你想在main中构造Foo(例如,因为那里有可用的参数(,那么在构造时使用make_shared并传递shared_ptr。

#include <iostream>
class Foo{
    ~Foo() { std::cout << "Foo destructed" << std::endl; }
};
class Bar{
public:
    void addFoo(std::shared_ptr<Foo> foo){
        auto my_foo = foo;
    }        
};
int main() {
    auto bar = Bar();
    bar.addFoo(std::make_shared<Foo>());
    return 0;
}

delete还会调用析构函数。您可以通过打印消息来测试共享指针是否会破坏对象,或者是否需要删除。

相关内容

  • 没有找到相关文章

最新更新