dyp*_*dyp 7 c++ shared-ptr language-lawyer c++11
它 广泛 知道,你可以使用shared_ptr
一个存储指向不完全类型,只要指针可以(有良好定义的行为)的施工过程中被删除shared_ptr
.例如,PIMPL技术:
struct interface
{
interface(); // out-of-line definition required
~interface() = default; // public inline member, even if implicitly defined
void foo();
private:
struct impl; // incomplete type
std::shared_ptr<impl> pimpl; // pointer to incomplete type
};
Run Code Online (Sandbox Code Playgroud)
[main.cpp中]
int main()
{
interface i;
i.foo();
}
Run Code Online (Sandbox Code Playgroud)
[interface.cpp]
struct interface::impl
{
void foo()
{
std::cout << "woof!\n";
}
};
interface::interface()
: pimpl( new impl ) // `delete impl` is well-formed at this point
{}
void interface::foo()
{
pimpl->foo();
}
Run Code Online (Sandbox Code Playgroud)
这个作品作为一个"删除器对象" "所有者对象"(*)所述的构建过程中被创建shared_ptr
在pimpl( new impl )
,和内部的类型擦除后存储shared_ptr
.此"所有者对象"稍后用于销毁指向的对象.这就是为什么提供内联析构函数应该是安全的原因interface
.
问题:标准在哪里保证它是安全的?
(*)在标准方面不是删除器,见下文,但它可以调用自定义删除器或调用delete-expression.该对象通常作为簿记对象的一部分存储,应用类型擦除并在虚函数中调用自定义删除/删除表达式.此时,delete-expression也应该是格式良好的.
参考github存储库中的最新草案(94c8fc71,修改N3797),[util.smartptr.shared.const]
Run Code Online (Sandbox Code Playgroud)template<class Y> explicit shared_ptr(Y* p);
3要求:
p
应可转换为T*
.Y
应该是一个完整的类型.表达式delete p
应格式良好,具有明确定义的行为,不得抛出异常.4效果:构造
shared_ptr
拥有指针的对象p
.5后置条件:
use_count() == 1 && get() == p
.6抛出:
bad_alloc
或无法获取内存以外的资源时的实现定义的异常.
注意:对于此ctor,shared_ptr
不需要拥有删除器.通过删除,标准似乎意味着自定义删除,例如您在构造期间提供的附加参数(或shared_ptr
从另一个参数获取/共享shared_ptr
,例如通过复制分配).另见(另见[util.smartptr.shared.const]/9).实现(boost,libstdc ++,MSVC,我猜每个理智的实现)总是存储"所有者对象".
由于删除器是自定义删除器,因此如果没有自定义删除器,则析构函数shared_ptr
是根据delete
(delete-expression)定义的:
[util.smartptr.shared.dest]
Run Code Online (Sandbox Code Playgroud)~shared_ptr();
1效果:
- 如果
*this
为空或与另一个shared_ptr
实例(use_count() > 1
)共享所有权,则没有副作用.- 否则,如果
*this
拥有一个对象p
和一个删除器d
,d(p)
则被调用.- 否则,
*this
拥有一个指针p
,并被delete p
调用.
我假设意图是需要一个实现来正确删除存储的指针,即使在shared_ptr
dtor 的范围内,delete-expression是格式错误的或者会调用UB.(delete-expression必须格式正确,并且在ctor中有明确定义的行为.)所以,问题是
问题:这需要在哪里?
(或者我太挑剔了,而且很明显,实现是否需要使用"所有者对象"?)
问: 哪里需要这个?
如果不需要,析构函数将具有未定义的行为,并且标准不习惯要求未定义的行为:-)
如果满足构造函数的先决条件,则析构函数将不会调用未定义的行为。实现如何确保这一点尚未指定,但您可以假设它是正确的,并且您不需要知道如何实现。如果预计实现不会做正确的事,那么析构函数就会有一个先决条件。
(或者我是不是太挑剔了,很明显,实现需要使用“所有者对象”?)
是的,必须创建一些额外的对象来拥有指针,因为引用计数(或其他簿记数据)必须位于堆上,而不是任何特定shared_ptr
实例的一部分,因为它可能需要比任何特定实例更长寿。所以是的,有一个额外的对象,它拥有指针,您可以将其称为所有者对象。如果用户没有提供删除器,则该所有者对象仅调用delete
. 例如:
template<typename T>
struct SpOwner {
long count;
long weak_count;
T* ptr;
virtual void dispose() { delete ptr; }
// ...
};
template<typename T, typename Del>
struct SpOwnerWithDeleter : SpOwner<T> {
Del del;
virtual void dispose() { del(this->ptr); }
// ...
};
Run Code Online (Sandbox Code Playgroud)
现在 ashared_ptr
有 a SpOwner*
,当计数降到零时,它会调用虚拟函数,dispose()
该函数会调用delete
或调用删除器,具体取决于对象的构造方式。是否构造 anSpOwner
或 an的决定SpOwnerWithDeleter
是在构造时做出的shared_ptr
,并且在销毁时该类型仍然相同shared_ptr
,因此如果它需要处置所拥有的指针,那么它将做正确的事情。
归档时间: |
|
查看次数: |
282 次 |
最近记录: |