为什么我的 unique_ptr 立即删除了自己?
Why is my unique_ptr immediately deleting itself?
我正在研究几个我还很陌生的概念。我想做的是依赖使用唯一指针将“Screen”对象注入私有成员“TempCtrl::mScreen”。我相信我正确地实现了设计模式,但我从来没有用唯一的指针来做到这一点,而且指针似乎在 TempCtrl 构造函数调用之前被删除了。为什么会这样?
主要函数摘录:
#include "tempctrl.hpp"
#include "screen.hpp"
#include <memory>
int main()
{
std::unique_ptr<Screen> _Screen(new Screen);
TempCtrl tc(_Screen);
/* ... */
}
TempCtrl 构造函数声明摘录:
class TempCtrl
{
public:
TempCtrl(std::unique_ptr<Screen> _Screen);
~TempCtrl();
private:
std::unique_ptr<Screen> mScreen;
};
TempCtrl 实现摘录:
TempCtrl::TempCtrl(std::unique_ptr<Screen> _Screen)
: mScreen(_Screen)
{
}
编译器输出:
/usr/bin/g++ -std=c++11 -g -c screen.cpp tempctrl.cpp main.cpp
tempctrl.cpp: In constructor 'TempCtrl::TempCtrl(std::unique_ptr<Screen>)':
tempctrl.cpp:6:18: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Screen; _Dp = std::default_delete<Screen>]'
: mScreen(_Screen)
^
In file included from /usr/include/c++/8/memory:80,
from tempctrl.hpp:14,
from tempctrl.cpp:1:
/usr/include/c++/8/bits/unique_ptr.h:394:7: note: declared here
unique_ptr(const unique_ptr&) = delete;
^~~~~~~~~~
main.cpp: In function 'int main()':
main.cpp:9:22: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Screen; _Dp = std::default_delete<Screen>]'
TempCtrl tc(_Screen);
^
In file included from /usr/include/c++/8/memory:80,
from tempctrl.hpp:14,
from main.cpp:1:
/usr/include/c++/8/bits/unique_ptr.h:394:7: note: declared here
unique_ptr(const unique_ptr&) = delete;
^~~~~~~~~~
In file included from main.cpp:1:
tempctrl.hpp:68:3: note: initializing argument 1 of 'TempCtrl::TempCtrl(std::unique_ptr<Screen>)'
TempCtrl(std::unique_ptr<Screen> _Screen);
^~~~~~~~
make: *** [Makefile:14: *.o] Error 1
使用已删除的函数并不意味着某些东西会自行删除。
C++ 中的删除函数是这样一种函数,当 C++ 编译器认为它应该调用它时,它会故意生成一个错误。
在这里,你删除的函数是你的unique_ptr
的“复制构造函数”。
复制构造函数是您在 C++ 中获取一个对象并制作它的另一个副本的方式。
unique_ptr
应该是唯一的。复制独特的东西是违法的。
您尝试调用该构造函数...试图复制 unique_ptr
。
std::unique_ptr<Screen> _Screen(new Screen);
TempCtrl tc(_Screen); // <— here
这里有唯一指针_Screen
1。它唯一且仅拥有 new Screen
资源。
然后您创建一个 TempCtrl
。 TempCtrl tc
的构造函数按值获取 unique_ptr<Screen>
-- 这是另一个唯一指针。
因此,当您调用该构造函数时,C++ 编译器会尝试复制 unique_ptr
,并向您喷出错误,意思是“不允许您这样做”。
要么您必须将 unique_ptr<Screen>
移动(转让所有权)到 TempCtrl
:
TempCtrl tc(std::move(_Screen));
或者,您必须通过引用传递 unique_ptr
:
TempCtrl(std::unique_ptr<Screen> const & _screen);
或者,只传递原始指针:
TempCtrl(Screen* _screen);
这些又需要更改其他代码才能使其正常工作。在控件内存储指向屏幕的唯一指针意味着该控件(而不是其他控件)拥有该屏幕。这似乎很奇怪。
unique_ptr
意味着只有一个智能指针在指向对象的整个生命周期内拥有唯一和完整的所有权。因此,对同一个对象有两个唯一的指针是无稽之谈。
您似乎已经习惯了垃圾收集语言。在 C++ 中,您对与之交互的每个对象的生命周期负责。智能指针可以帮助解决这个问题,但是 C++ 中 没有智能指针 可以让你不关心对象的生命周期(盲目和不假思索地,为此目的使用共享指针只会让人无法理解对象生命周期错误更难找到)。
这会给您带来额外的认知负担,这是您不习惯思考的事情。 C++ 中有一些技术可以减少这种负载,但学习它们并非易事。
这可能是 shared_ptr
或 weak_ptr
正确的情况之一,但我不确定。只要任何控件保持活动状态,屏幕就保持活动状态很奇怪,而一个控件比它所在的屏幕更持久也很奇怪。
我怀疑控件应该存在于布局中,只有在绘图时它们才会从布局中获取屏幕。布局将管理控件的生命周期。并且绘图功能将存在于布局之外,并调用带有屏幕的布局进行绘制。
思考应该由哪个代码负责、拥有和管理哪些其他代码在工作。祝你好运。
1 顺便说一句,_Screen
这个名字是 C++ 实现保留的;你使用它是非法的。不要以 _
后跟大写字母作为标识符的开头,这会使您的程序格式错误,但不需要诊断)。人们复制系统头文件, 允许使用它们,因为它们是由编译器供应商编写的,并以这种方式制作格式错误的程序。
我正在研究几个我还很陌生的概念。我想做的是依赖使用唯一指针将“Screen”对象注入私有成员“TempCtrl::mScreen”。我相信我正确地实现了设计模式,但我从来没有用唯一的指针来做到这一点,而且指针似乎在 TempCtrl 构造函数调用之前被删除了。为什么会这样?
主要函数摘录:
#include "tempctrl.hpp"
#include "screen.hpp"
#include <memory>
int main()
{
std::unique_ptr<Screen> _Screen(new Screen);
TempCtrl tc(_Screen);
/* ... */
}
TempCtrl 构造函数声明摘录:
class TempCtrl
{
public:
TempCtrl(std::unique_ptr<Screen> _Screen);
~TempCtrl();
private:
std::unique_ptr<Screen> mScreen;
};
TempCtrl 实现摘录:
TempCtrl::TempCtrl(std::unique_ptr<Screen> _Screen)
: mScreen(_Screen)
{
}
编译器输出:
/usr/bin/g++ -std=c++11 -g -c screen.cpp tempctrl.cpp main.cpp
tempctrl.cpp: In constructor 'TempCtrl::TempCtrl(std::unique_ptr<Screen>)':
tempctrl.cpp:6:18: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Screen; _Dp = std::default_delete<Screen>]'
: mScreen(_Screen)
^
In file included from /usr/include/c++/8/memory:80,
from tempctrl.hpp:14,
from tempctrl.cpp:1:
/usr/include/c++/8/bits/unique_ptr.h:394:7: note: declared here
unique_ptr(const unique_ptr&) = delete;
^~~~~~~~~~
main.cpp: In function 'int main()':
main.cpp:9:22: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Screen; _Dp = std::default_delete<Screen>]'
TempCtrl tc(_Screen);
^
In file included from /usr/include/c++/8/memory:80,
from tempctrl.hpp:14,
from main.cpp:1:
/usr/include/c++/8/bits/unique_ptr.h:394:7: note: declared here
unique_ptr(const unique_ptr&) = delete;
^~~~~~~~~~
In file included from main.cpp:1:
tempctrl.hpp:68:3: note: initializing argument 1 of 'TempCtrl::TempCtrl(std::unique_ptr<Screen>)'
TempCtrl(std::unique_ptr<Screen> _Screen);
^~~~~~~~
make: *** [Makefile:14: *.o] Error 1
使用已删除的函数并不意味着某些东西会自行删除。
C++ 中的删除函数是这样一种函数,当 C++ 编译器认为它应该调用它时,它会故意生成一个错误。
在这里,你删除的函数是你的unique_ptr
的“复制构造函数”。
复制构造函数是您在 C++ 中获取一个对象并制作它的另一个副本的方式。
unique_ptr
应该是唯一的。复制独特的东西是违法的。
您尝试调用该构造函数...试图复制 unique_ptr
。
std::unique_ptr<Screen> _Screen(new Screen);
TempCtrl tc(_Screen); // <— here
这里有唯一指针_Screen
1。它唯一且仅拥有 new Screen
资源。
然后您创建一个 TempCtrl
。 TempCtrl tc
的构造函数按值获取 unique_ptr<Screen>
-- 这是另一个唯一指针。
因此,当您调用该构造函数时,C++ 编译器会尝试复制 unique_ptr
,并向您喷出错误,意思是“不允许您这样做”。
要么您必须将 unique_ptr<Screen>
移动(转让所有权)到 TempCtrl
:
TempCtrl tc(std::move(_Screen));
或者,您必须通过引用传递 unique_ptr
:
TempCtrl(std::unique_ptr<Screen> const & _screen);
或者,只传递原始指针:
TempCtrl(Screen* _screen);
这些又需要更改其他代码才能使其正常工作。在控件内存储指向屏幕的唯一指针意味着该控件(而不是其他控件)拥有该屏幕。这似乎很奇怪。
unique_ptr
意味着只有一个智能指针在指向对象的整个生命周期内拥有唯一和完整的所有权。因此,对同一个对象有两个唯一的指针是无稽之谈。
您似乎已经习惯了垃圾收集语言。在 C++ 中,您对与之交互的每个对象的生命周期负责。智能指针可以帮助解决这个问题,但是 C++ 中 没有智能指针 可以让你不关心对象的生命周期(盲目和不假思索地,为此目的使用共享指针只会让人无法理解对象生命周期错误更难找到)。
这会给您带来额外的认知负担,这是您不习惯思考的事情。 C++ 中有一些技术可以减少这种负载,但学习它们并非易事。
这可能是 shared_ptr
或 weak_ptr
正确的情况之一,但我不确定。只要任何控件保持活动状态,屏幕就保持活动状态很奇怪,而一个控件比它所在的屏幕更持久也很奇怪。
我怀疑控件应该存在于布局中,只有在绘图时它们才会从布局中获取屏幕。布局将管理控件的生命周期。并且绘图功能将存在于布局之外,并调用带有屏幕的布局进行绘制。
思考应该由哪个代码负责、拥有和管理哪些其他代码在工作。祝你好运。
1 顺便说一句,_Screen
这个名字是 C++ 实现保留的;你使用它是非法的。不要以 _
后跟大写字母作为标识符的开头,这会使您的程序格式错误,但不需要诊断)。人们复制系统头文件, 允许使用它们,因为它们是由编译器供应商编写的,并以这种方式制作格式错误的程序。