典型的工厂设计模式要求基类声明虚拟析构函数,但实际上可以使用shared_ptr
来避免这种情况。
#include <iostream>
#include <memory>
#include <cstdio>
using namespace std;
class Dog {
public:
~Dog() { cout << "dog destroyed\n"; }
};
class Yellowdog : public Dog {
public:
~Yellowdog() { cout << "Yellow dog destroyed.\n"; }
};
class DogFactory {
public:
static shared_ptr<Dog> createYellowdog() {
return shared_ptr<Yellowdog>(new Yellowdog());
}
};
int main(int argc, char *argv[]) {
auto ptr = DogFactory::createYellowdog();
cout << ptr.use_count() << endl;
return 0;
}
在这种情况下,输出为yellowdog destroyed
,后跟dog destroyed
。但为什么?为什么使用shared_ptr
可以在~Dog
之前省略虚拟关键字?
答案 0 :(得分:3)
这种情况正在发生,因为$csvfile = 'csvfile.csv';
if (file_exists($csvfile)) {
unlink($csvfile);
echo "Alte $csvfile entfernt!";
} else {
echo "Keine alte $csvfile vorhanden";
}
$row = 0;
$fp = fopen('csvfile.csv', 'w');
if (($handle = fopen("oldCSV.csv", "r")) !== FALSE) {
while (($data = fgetcsv($handle, 200000, ",")) !== FALSE) {
$num = count($data);
$dataold = ['J', 'N'];
$datanew = ['5', '0'];
echo "<p> $num Felder in Zeile $row: <br /></p>\n";
$row++;
for ($c=0; $c < $num; $c++) {
fputs($fp, str_replace($dataold, $datanew, $data[$c] . "\n"));
}
}
}
fclose($fp);
在控制块中存储了类型删除的删除器,该删除器是在创建第一个shared_ptr
时创建的。在你的情况下,为黄狗创建shared_ptr
,删除器是为了调用黄狗析构函数。
当您将一个shared_ptr
复制(或复制 - 构建)到另一个时,它们共享相同的控制块,而新的共享ptr将从原始控制块调用删除器 - 一个会叫做yellowdog析构函数的人。
但是,它并没有真正使类具有多态性并适合工厂实现 - 类中的任何其他非虚函数都将基于静态类型shared_ptr
调用,而您不希望这在多态类中。
答案 1 :(得分:0)
SergeyA 说的完全正确,对于那些想看到真正代码的人来说, 这是来自 LLVM 的 shared_ptr 实现(我只展示了与解释相关的部分代码):
ItemId UnitId UnitPrice Discount Tax Duty Type TotalPriceIn QuantityIn QuantityOut
10 1 10 2 1 0 1 19 2 1
第 1 步:
当您调用 template<class _Tp>
class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS shared_ptr
{
//Constructor taking a pointer
template<class _Yp, class = _EnableIf<
_And<__compatible_with<_Yp, _Tp>>::value>
>
explicit shared_ptr(_Yp* __p) : __ptr_(__p) {
unique_ptr<_Yp> __hold(__p);
typedef typename __shared_ptr_default_allocator<_Yp>::type _AllocT;
typedef __shared_ptr_pointer<_Yp*, __shared_ptr_default_delete<_Tp, _Yp>, _AllocT > _CntrlBlk;
__cntrl_ = new _CntrlBlk(__p, __shared_ptr_default_delete<_Tp, _Yp>(), _AllocT());
__hold.release();
__enable_weak_this(__p, __p);
}
...
//other code omited
}
//Move Constructor
template<class _Tp>
template<class _Yp>
inline
shared_ptr<_Tp>::shared_ptr(shared_ptr<_Yp>&& __r,
typename enable_if<__compatible_with<_Yp, element_type>::value, __nat>::type)
_NOEXCEPT
: __ptr_(__r.__ptr_),
__cntrl_(__r.__cntrl_)
{
__r.__ptr_ = nullptr;
__r.__cntrl_ = nullptr;
}
时,首先将调用带指针的构造函数,在这种情况下,_Tp 与 _Yp 相同。还创建了一个控制块,将 _Yp*(所以 Yellowdog*)作为输入,并将其保存在里面。然后将创建的控制块作为其虚拟接口(类型擦除)保存在 shared_ptr
第 2 步:
然后在函数返回期间 shared_ptr<Yellowdog>(new Yellowdog());
被转换为 shared_ptr<Yellowdog>
。
在这种情况下,shared_ptr<Dog>
的移动构造函数被调用,然而,这次_Tp 是Dog,_Yp 是Yellowdog。因此 shared_ptr<Dog>
发生了从 Yellowdog* 到 Dog* 的隐式转换。但对于 __ptr_
来说,它只是一个简单的副本,没有任何变化。
第 3 步:
在析构过程中,会调用 __ctrl_
中的解除分配器,并且由于它从一开始就保存了 Yellowdog*,因此它可以直接调用其析构函数,进而调用其父(Dog)析构函数。
因此,在这种情况下,Dog 不需要虚拟析构函数。