c++ - 需要使用 shared_ptr 吗?
问题描述
我有两个类:Lattice 和 ModelGUI。我想将函数从 Lattice 作为回调传递给 GUI。我将 Lattice 实现为 unique_ptr。一些代码:
模型GUI.h:
using CheckTypeClbk = std::function<Enums::AgentType(int)>;
ModelGUI(const Matrix* matrix_, CheckTypeClbk callback, float windowHeight_, float windowWidth_, float latticeWidth_);
主.cpp:
std::unique_ptr<ILattice> lattice(new Lattice(5, qMap));
ModelGUI gui(lattice->getLattice(), std::bind(&ILattice::checkAgentType, lattice, std::placeholders::_1),
800, 1200, 800);
通过这个实现,我得到了关于模板的奇怪编译错误:
1>main.cpp
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\xutility(390): error C2664: 'std::tuple<std::unique_ptr<ILattice,std::default_delete<_Ty>>,std::_Ph<1>>::tuple(std::tuple<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::_Ph<1>> &&)': cannot convert argument 1 from 'std::unique_ptr<ILattice,std::default_delete<_Ty>>' to 'std::allocator_arg_t'
1> with
1> [
1> _Ty=ILattice
1> ]
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\xutility(389): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\functional(1902): note: see reference to function template instantiation 'std::_Compressed_pair<Enums::AgentType (__cdecl ILattice::* )(int),std::tuple<std::unique_ptr<ILattice,std::default_delete<_Ty>>,std::_Ph<1>>,false>::_Compressed_pair<Enums::AgentType(__cdecl ILattice::* )(int),_Cv_TiD&,const std::_Ph<1>&>(std::_One_then_variadic_args_t,_Other1 &&,_Cv_TiD &,const std::_Ph<1> &)' being compiled
1> with
1> [
1> _Ty=ILattice,
1> _Cv_TiD=std::unique_ptr<ILattice,std::default_delete<ILattice>>,
1> _Other1=Enums::AgentType (__cdecl ILattice::* )(int)
1> ]
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\functional(1903): note: see reference to function template instantiation 'std::_Compressed_pair<Enums::AgentType (__cdecl ILattice::* )(int),std::tuple<std::unique_ptr<ILattice,std::default_delete<_Ty>>,std::_Ph<1>>,false>::_Compressed_pair<Enums::AgentType(__cdecl ILattice::* )(int),_Cv_TiD&,const std::_Ph<1>&>(std::_One_then_variadic_args_t,_Other1 &&,_Cv_TiD &,const std::_Ph<1> &)' being compiled
1> with
1> [
1> _Ty=ILattice,
1> _Cv_TiD=std::unique_ptr<ILattice,std::default_delete<ILattice>>,
1> _Other1=Enums::AgentType (__cdecl ILattice::* )(int)
1> ]
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\functional(1902): note: while compiling class template member function 'std::_Binder<std::_Unforced,Enums::AgentType (__cdecl ILattice::* )(int),std::unique_ptr<ILattice,std::default_delete<_Ty>> &,const std::_Ph<1> &>::_Binder(_Fx &&,std::unique_ptr<_Ty,std::default_delete<_Ty>> &,const std::_Ph<1> &)'
1> with
1> [
1> _Ty=ILattice,
1> _Fx=Enums::AgentType (__cdecl ILattice::* )(int)
1> ]
1>d:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.14.26428\include\functional(1929): note: see reference to function template instantiation 'std::_Binder<std::_Unforced,Enums::AgentType (__cdecl ILattice::* )(int),std::unique_ptr<ILattice,std::default_delete<_Ty>> &,const std::_Ph<1> &>::_Binder(_Fx &&,std::unique_ptr<_Ty,std::default_delete<_Ty>> &,const std::_Ph<1> &)' being compiled
1> with
1> [
1> _Ty=ILattice,
1> _Fx=Enums::AgentType (__cdecl ILattice::* )(int)
1> ]
1>d:\predator-prey\predator-prey\main.cpp(16): note: see reference to class template instantiation 'std::_Binder<std::_Unforced,Enums::AgentType (__cdecl ILattice::* )(int),std::unique_ptr<ILattice,std::default_delete<_Ty>> &,const std::_Ph<1> &>' being compiled
1> with
1> [
1> _Ty=ILattice
1> ]
但是当我使用 shared_ptr 而不是 unique_ptr 时,一切正常。是一个好习惯吗?我听说要尽可能避免 shared_ptr ,除非它们是完全必要的。
解决方案
你需要shared_ptr
吗?
不,至少对于给定的例子来说不是。
如果lattice
和gui
被定义在不同的范围内,具有不同的生命周期并被广泛使用,wowie-zowie,我们可以谈论shared_ptr
.
为什么?
让我们从一个非常简单的例子开始,说明为什么unique_ptr
会导致悲伤。
#include <functional>
#include <iostream>
struct test
{
test() = default;
test(const test &)
{
std::cout << "copied" << std::endl;
}
void func(int i)
{
std::cout << i << std::endl;
}
};
int main()
{
test t;
std::function<void(int)> f1 = std::bind(&test::func, t, std::placeholders::_1);
f1(1);
}
test
除了告诉我们何时复制对象并证明函数运行之外,它并没有做太多事情。执行它,我们将看到它t
被复制并从函数产生了预期的输出。
std::unique_ptr
不能复制,因为这几乎会破坏工作描述的整个独特部分。我们看到,如果我们稍微改变main
一下以使用 aunique_ptr
并更接近提出的问题。
int main()
{
std::unique_ptr<test> tp = std::make_unique<test>();
std::function<void(int)> f1 = std::bind(&test::func, tp, std::placeholders::_1);
}
正如预期的那样,这不会编译。我们可以通过使用std::reference_wrapper
std::function<void(int)> f1 = std::bind(&test::func, std::reference_wrapper<std::unique_ptr<test>>(tp), std::placeholders::_1);
或提供一个原始指针bind
std::function<void(int)> f1 = std::bind(&test::func, tp.get(), std::placeholders::_1); f1(1);
但这需要tp
有更广泛的范围并保证寿命f1
。这真正归结为为什么比test t;
一开始使用更多?我们真的需要一个指针吗?
但是现在让我们先去吧,因为我们至少可以在我们前往更绿色的牧场之前让它看起来更漂亮。这与 lambda 表达式相同
std::function<void(int)> f1 = [&tp](int i) { tp->func(i); };
通常我不是“Lambda 比 Lambda 更容易阅读”的拥护者bind
,但这个案例是一个非常有说服力的论点。
回归本源,其实并没有什么不同
int main()
{
test t;
std::function<void(int)> f1 = [&t](int i) { t.func(i); };
f1(1);
}
并完全消除了指针。没有指针,没有shared_ptr
。
如果t
可以一劳永逸,唯一的用户就是回调,让 lambda 随身携带一份副本,t
让原来的死掉。
std::function<void(int)> scopedemo()
{
test t;
return [t](int i) mutable { t.func(i); }; //
}
int main()
{
auto f1 = scopedemo();
f1(1);
}
注意mutable
. Lambda 默认携带常量,不能用于调用非const
方法或用作非const
参数。
推荐阅读
- c++ - 无法添加视口 Unreal C++
- r - RODBC sqlQuery 每次运行时都会返回不同数量的行
- javascript - 为什么 JavaScript 会错误地解析数据?
- redis - 我可以用 Cosmos DB 替换 Redis 缓存吗?
- keras - 我想知道我们如何将分类变量作为输入到 keras 中的嵌入层并训练该嵌入层?
- c++ - 检查类型的相等性
- python - 如何使用网格在 tkinter 中显示图像
- mysql - MySQL查询在没有可用数据时获取行数,添加where子句时跳过结果
- ruby-on-rails - 调整 ActiveStorage blob 大小时如何仅处理 gif 的第一帧
- javascript - 获取块 onclick 中的下一个孩子