c ++ shared_ptr in polymorphism without virtual destructor

时间:2016-04-28 16:52:48

标签: c++ polymorphism factory

典型的工厂设计模式要求基类声明虚拟析构函数,但实际上可以使用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之前省略虚拟关键字?

2 个答案:

答案 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 不需要虚拟析构函数。