当 Base class 具有受保护的析构函数时创建 unique_ptr<Base>
Creating unique_ptr<Base> when Base class has a protected destructor
class Base {
public:
Base() {}
virtual void print()const = 0;
protected:
virtual ~Base() { std::cout << "Base destructor\n\n"; }
};
int main()
{
//std::vector<std::unique_ptr<Base>> v1;
//The line above won't compile because: 'Base::~Base': cannot access protected member declared in class 'Base'
std::vector<std::shared_ptr<Base>> v2;
return 0;
}
当我创建向量时试图调用析构函数的是什么?为什么它不会为 unique_ptr 向量编译但会为 shared_ptr 向量编译?
std::unique_ptr
无法访问 Base
的析构函数,因为它是 protected
。 std::shared_ptr
使用多态删除器,因此 std::shared_ptr
只需要在创建新的 std::shared_ptr
.
时访问 Base
的析构函数
// this fails because the destructor of Base is inaccessible
std::unique_ptr<Base> a;
// this is ok because the destructor isn't required to instantiate the type
std::shared_ptr<Base> b;
// this fails because make_shared needs the destructor
std::shared_ptr<Base> c = std::make_shared<Base>();
"Polymorphic deleter" 基本上意味着 std::shared_ptr
存储指向销毁对象的函数的指针。 std::unique_ptr
使用 "static deleter" 直接销毁对象。这是一些伪代码:
struct shared_ptr {
~shared_ptr() {
deleter();
}
void (*deleter)(); // pointer to function that destroys the object
};
// shared_ptr doesn't try to call the destructor directly so we don't need access
// so this is ok
shared_ptr a;
shared_ptr make_shared() {
// here we generate (with templates) a function that calls Base::~Base
// then we set "deleter" to point to that function
// the destructor has to be accessible for us to do this
}
// so we get an error here
shared_ptr b = make_shared();
struct unique_ptr {
~unique_ptr() {
// unique_ptr calls the Base destructor directly
// unique_ptr needs access to the destructor to instantiate the type
}
};
// so we get an error here
unique_ptr c;
在你的情况下,Base
恰好是抽象的,所以你可以使用 std::shared_ptr<Base>
,因为你永远不需要写 std::make_shared<Base>()
。只要 Base
的子类有 public
个析构函数,std::make_shared
就可以访问它们而不会出错。
局部变量v1
和v2
具有自动存储期限,超出范围时会自动销毁。 std::vector
在这里无关紧要:在 vector::~vector()
内,编译器将为元素析构函数生成代码。即使向量始终为空(这是 运行 次 属性!),仍然必须生成此代码。所以让我们简化代码:
std::unique_ptr<Base> v1;
std::shared_ptr<Base> v2;
当v1
超出范围时,它必须被销毁。编译器生成的析构函数归结为 (*):
~unique_ptr() {
delete ptr;
}
要为 delete ptr
生成代码,编译器需要可访问的析构函数。被保护了,所以编译失败。
现在让我们看看v2
。编译器也必须生成析构函数。但是 shared_ptr
有一个用于托管对象的类型擦除删除器。这意味着将间接调用托管对象析构函数——通过虚函数:
struct shared_ptr_deleter_base {
virtual void destroy() = 0;
virtual ~shared_ptr_deleter_base() = default;
};
~shared_ptr() {
// member shared_ptr::deleter has type shared_ptr_deleter_base*
if (deleter)
deleter->destroy();
}
要为 deleter->destroy()
生成代码,您根本不需要访问 Base::~Base()
。 shared_ptr
的默认构造函数只是将 deleter
设置为空指针:
shared_ptr() {
deleter = nullptr;
}
这就是 std::shared_ptr<Base> v2;
编译的原因:不仅 Base::~Base()
在 运行 时不被调用,编译器在编译时也不会生成任何调用。
让我们考虑这一行:
std::shared_ptr<Base> v2(new Base());
现在调用了下面的构造函数(注意它是一个带有单独参数U
的模板,可以不同于shared_ptr<T>
中的T
):
template<class U>
shared_ptr(U* ptr) {
deleter = new shared_ptr_deleter<U>(ptr);
}
这里 shared_ptr_deleter
是一个具体的 class 派生自 shared_ptr_deleter_base
:
template<class T>
struct shared_ptr_deleter : shared_ptr_deleter_base {
T* ptr;
shared_ptr_deleter(T* p) : ptr(p) {}
virtual void destroy() {
delete ptr;
}
};
要为采用 new Base()
的构造函数生成代码,编译器必须为 shared_ptr_deleter<Base>::destroy()
生成代码。现在它失败了,因为 Base::~Base()
无法访问。
(*) 我只提供简化的定义,只是为了展示基本思想,而没有深入讨论与理解所讨论问题无关的所有细节。
class Base {
public:
Base() {}
virtual void print()const = 0;
protected:
virtual ~Base() { std::cout << "Base destructor\n\n"; }
};
int main()
{
//std::vector<std::unique_ptr<Base>> v1;
//The line above won't compile because: 'Base::~Base': cannot access protected member declared in class 'Base'
std::vector<std::shared_ptr<Base>> v2;
return 0;
}
当我创建向量时试图调用析构函数的是什么?为什么它不会为 unique_ptr 向量编译但会为 shared_ptr 向量编译?
std::unique_ptr
无法访问 Base
的析构函数,因为它是 protected
。 std::shared_ptr
使用多态删除器,因此 std::shared_ptr
只需要在创建新的 std::shared_ptr
.
Base
的析构函数
// this fails because the destructor of Base is inaccessible
std::unique_ptr<Base> a;
// this is ok because the destructor isn't required to instantiate the type
std::shared_ptr<Base> b;
// this fails because make_shared needs the destructor
std::shared_ptr<Base> c = std::make_shared<Base>();
"Polymorphic deleter" 基本上意味着 std::shared_ptr
存储指向销毁对象的函数的指针。 std::unique_ptr
使用 "static deleter" 直接销毁对象。这是一些伪代码:
struct shared_ptr {
~shared_ptr() {
deleter();
}
void (*deleter)(); // pointer to function that destroys the object
};
// shared_ptr doesn't try to call the destructor directly so we don't need access
// so this is ok
shared_ptr a;
shared_ptr make_shared() {
// here we generate (with templates) a function that calls Base::~Base
// then we set "deleter" to point to that function
// the destructor has to be accessible for us to do this
}
// so we get an error here
shared_ptr b = make_shared();
struct unique_ptr {
~unique_ptr() {
// unique_ptr calls the Base destructor directly
// unique_ptr needs access to the destructor to instantiate the type
}
};
// so we get an error here
unique_ptr c;
在你的情况下,Base
恰好是抽象的,所以你可以使用 std::shared_ptr<Base>
,因为你永远不需要写 std::make_shared<Base>()
。只要 Base
的子类有 public
个析构函数,std::make_shared
就可以访问它们而不会出错。
局部变量v1
和v2
具有自动存储期限,超出范围时会自动销毁。 std::vector
在这里无关紧要:在 vector::~vector()
内,编译器将为元素析构函数生成代码。即使向量始终为空(这是 运行 次 属性!),仍然必须生成此代码。所以让我们简化代码:
std::unique_ptr<Base> v1;
std::shared_ptr<Base> v2;
当v1
超出范围时,它必须被销毁。编译器生成的析构函数归结为 (*):
~unique_ptr() {
delete ptr;
}
要为 delete ptr
生成代码,编译器需要可访问的析构函数。被保护了,所以编译失败。
现在让我们看看v2
。编译器也必须生成析构函数。但是 shared_ptr
有一个用于托管对象的类型擦除删除器。这意味着将间接调用托管对象析构函数——通过虚函数:
struct shared_ptr_deleter_base {
virtual void destroy() = 0;
virtual ~shared_ptr_deleter_base() = default;
};
~shared_ptr() {
// member shared_ptr::deleter has type shared_ptr_deleter_base*
if (deleter)
deleter->destroy();
}
要为 deleter->destroy()
生成代码,您根本不需要访问 Base::~Base()
。 shared_ptr
的默认构造函数只是将 deleter
设置为空指针:
shared_ptr() {
deleter = nullptr;
}
这就是 std::shared_ptr<Base> v2;
编译的原因:不仅 Base::~Base()
在 运行 时不被调用,编译器在编译时也不会生成任何调用。
让我们考虑这一行:
std::shared_ptr<Base> v2(new Base());
现在调用了下面的构造函数(注意它是一个带有单独参数U
的模板,可以不同于shared_ptr<T>
中的T
):
template<class U>
shared_ptr(U* ptr) {
deleter = new shared_ptr_deleter<U>(ptr);
}
这里 shared_ptr_deleter
是一个具体的 class 派生自 shared_ptr_deleter_base
:
template<class T>
struct shared_ptr_deleter : shared_ptr_deleter_base {
T* ptr;
shared_ptr_deleter(T* p) : ptr(p) {}
virtual void destroy() {
delete ptr;
}
};
要为采用 new Base()
的构造函数生成代码,编译器必须为 shared_ptr_deleter<Base>::destroy()
生成代码。现在它失败了,因为 Base::~Base()
无法访问。
(*) 我只提供简化的定义,只是为了展示基本思想,而没有深入讨论与理解所讨论问题无关的所有细节。