C++ 我应该在类构造函数内部还是外部初始化共享\u ptr?
给出以下示例代码:C++ 我应该在类构造函数内部还是外部初始化共享\u ptr?,c++,c++11,smart-pointers,C++,C++11,Smart Pointers,给出以下示例代码: #include <memory> class Foo { public: Foo(std::shared_ptr<int> p); private: std::shared_ptr<int> ptr; }; Foo::Foo(std::shared_ptr<int> p) : ptr(std::move(p)) { } class Bar { public: Bar(int &p); pr
#include <memory>
class Foo {
public:
Foo(std::shared_ptr<int> p);
private:
std::shared_ptr<int> ptr;
};
Foo::Foo(std::shared_ptr<int> p) : ptr(std::move(p)) {
}
class Bar {
public:
Bar(int &p);
private:
std::shared_ptr<int> ptr;
};
Bar::Bar(int &p) : ptr(std::make_shared<int>(p)) {
}
int main() {
Foo foo(std::make_shared<int>(int(256)));
Bar bar(*new int(512));
return 0;
}
#包括
福班{
公众:
Foo(std::shared_ptr p);
私人:
std::共享的ptr;
};
Foo::Foo(std::shared_ptr p):ptr(std::move(p)){
}
分类栏{
公众:
酒吧(国际及太平洋);;
私人:
std::共享的ptr;
};
酒吧:酒吧(内部和外部):ptr(标准:共享){
}
int main(){
Foo-Foo(std::make_shared(int(256));
条形图(*新整数(512));
返回0;
}
Foo和Bar都能正常工作。但是,在调用构造函数时创建共享的_ptr,然后使用std::move转移所有权,然后只传递对对象的引用并将共享的_ptr的创建委托给类构造函数,这两者之间有什么区别吗
我认为第二种方法更好,因为我不必移动指针。然而,我在阅读的代码中基本上看到了第一种方法
我应该使用哪一个?为什么?Foo是正确的
酒吧是令人憎恶的。它涉及内存泄漏、不安全的异常行为和不必要的拷贝
编辑:内存泄漏的解释
解构这条线:
Bar bar(*new int(512));
这些操作的结果:
这取决于你想要实现什么 如果您在内部需要一个
shared\u ptr
,因为您想与稍后创建的其他对象共享该对象,那么第二种方法可能更好(除了那可怕的构造函数调用)
如果您想要共享现有对象的所有权(实际上,这是更常见的情况),您没有选择,您需要使用第一种方法
如果这两种方法都不适用,那么首先您可能不需要共享的ptr。第二种方法是不正确的;它泄漏内存。与
Bar::Bar(int &p) : ptr(std::make_shared<int>(p)) {
}
....
Bar bar(*new int(512));
…但看看这个这纯粹是邪恶的。没有人期望构造函数的引用参数要求它引用新对象将负责的堆对象
你可以写得更理智些
Bar::Bar(int *p) : ptr(p) {
}
....
Bar bar(new int(512));
事实上,如果您愿意,您可以给
Foo
第二个构造函数来实现这一点。函数签名使指针必须指向堆分配的对象变得多么清晰,这是一个有争议的问题,但是std::shared_ptr
提供了同样的功能,因此有先例。这取决于您的类所做的工作,这是否是一个好主意。两者都可以正确工作,但在main
中使用它们的方式并不一致
当我看到一个构造函数获取一个引用(比如Bar(int&p)
)时,我希望Bar保存一个引用。当我看到Bar(const int&p)
时,我希望它能保存一份副本。
当我看到一个右值ref(不是通用的,比如Bar(int&&p)
时,我希望p在传递后不会“保留其内容”(好吧……对于it来说,这并没有什么意义……)
在任何情况下,p
持有一个int
,而不是一个指针,make_shared期望的是转发给int构造函数的参数(即…一个int,而不是int*)
你的主要目标是什么
Foo foo(std::make_shared<int>(int(256)));
Bar bar(512);
Foo-Foo(std::make_shared(int(256));
棒材(512);
这将使bar保存动态分配的值512
的可共享副本
如果你做了Bar(*new int(512))
你让Bar保存一个“new int”的副本,它的指针被丢弃,因此int本身就泄漏了
一般来说,像*newsomething
这样的表达应该听起来像“no no no no no…”
但是您的Bar
构造函数有一个问题:为了能够接受常量或表达式返回值,它必须接受const int&
,而不是int&
如果您的意图是单独拥有堆分配的对象,我建议您接受std::unique\u ptr
。它清楚地记录了意图如果需要,您可以从std::unique_ptr
内部创建std::shared_ptr
:
#include <memory>
class Foo {
public:
Foo(std::unique_ptr<int> p);
private:
std::shared_ptr<int> ptr;
};
Foo::Foo(std::unique_ptr<int> p) : ptr(std::move(p)) {
}
int main() {
Foo foo(std::make_unique<int>(512));
}
#包括
福班{
公众:
Foo(std::unique_ptr p);
私人:
std::共享的ptr;
};
Foo::Foo(std::unique_ptr p):ptr(std::move(p)){
}
int main(){
Foo-Foo(std::make_unique(512));
}
不要使用Bar
,它很容易出错,并且无法描述您的意图(如果我正确理解您的意图)。假设您仅使用int
作为示例,并且存在真正的资源,那么这取决于您想要实现什么
第一种是典型的依赖注入,对象是通过构造函数注入的,好的一面是它可以简化单元测试
第二种情况只是在构造函数中创建对象,并使用通过构造函数传递的值对其进行初始化
顺便说一下,请注意如何初始化。这是:
Bar bar(*new int(512));
导致内存泄漏。内存已分配,但从未解除分配。我更喜欢使用std::make_shared
直接初始化。如果new
抛出错误,则表明存在明确的内存泄漏。代码泄漏内存,这两种情况完全不同。您可以更好地论证“内存泄漏”和“exeption unsafety”这很好,但问题不在于酒吧本身,而在于它的使用方式。但是你说的是酒吧,而真正的p
Bar bar(*new int(512));