Replacing/refactoring 个裸指针

Replacing/refactoring of naked pointers

我想在 class 继承情况下替换传统的裸指针用法。

我的意思的例子:

#include <iostream>
#include <vector>
#include <memory>

using namespace std;

class Base
{
public:
  void doBaseStuff ()
  {
    cout << "base stuff\n";
  }
};

class Derived:public Base
{
public:
  void doDerivedStuff ()
  {
    cout << "derived stuff\n";
  }
};


int main ()
{
 // vector to hold pointers
  vector < Base * >baseCollection;

// generate pointers
  Derived *derivedPtr = new Derived;
  Base* basePtr = new Base;
  
 // use derived pointer for something
  derivedPtr->doDerivedStuff ();
  
 // fill vector with pointers
  baseCollection.push_back (basePtr);
  baseCollection.push_back (derivedPtr);

 // iterate some 'interface' method
 for (auto & element:baseCollection)
 {
   element->doBaseStuff ();
 }

  return 0;
}

如下所示的重构方法是否正确?我使用 C++17。 std::unique_ptr 而不是 std::shared_ptr 在这种情况下不会编译(行:baseCollection.push_back(derivedPtr);)。

#include <iostream>
#include <vector>
#include <memory>

using namespace std;

// sample classes
class Base
{
public:
  void doBaseStuff ()
  {
    cout << "base stuff\n";
  }
};

class Derived:public Base
{
public:
  void doDerivedStuff ()
  {
    cout << "derived stuff\n";
  }
};


int main ()
{
 // vector to hold pointers
 vector<shared_ptr<Base>> baseCollection;
 
 // generate pointers
 shared_ptr<Base> basePtr = make_shared<Base>();
 shared_ptr<Derived> derivedPtr = make_shared<Derived>();
 
 // use derived pointer for something
 derivedPtr->doDerivedStuff();
 
 // fill vector with pointers
 baseCollection.push_back(basePtr);
 baseCollection.push_back(derivedPtr); // implicit cast from shared_ptr<Derived> to shared_ptr<Base> !?
 
 // iterate some 'interface' method
 for(auto& element : baseCollection)
 {
     element->doBaseStuff();
 }

  return 0;
}

正如预期的那样,两种情况下的输出如下:

derived stuff
base stuff
base stuff

您可以使用 std::unique_ptr 而不是 std::shared_ptr(确实推荐这样做,因为所有权已在您的应用程序中明确处理,并且不会未指定给 垃圾收集器),但您必须 将它们移动 到向量中才能转移(唯一)所有权。

 vector<unique_ptr<Base>> baseCollection;
 
 // generate pointers
 unique_ptr<Base> basePtr = make_unique<Base>();
 unique_ptr<Derived> derivedPtr = make_unique<Derived>();
 
 // use derived pointer for something
 derivedPtr->doDerivedStuff();
 
 // fill vector with pointers
 baseCollection.push_back(std::move(basePtr));
 baseCollection.push_back(std::move(derivedPtr));

当然,之后您不能再使用 basePtrderivedPtr 变量,因为它们不再拥有这些对象。

另一种方法是使用临时对象:

 baseCollection.push_back(make_unique<Base>());
 baseCollection.push_back(make_unique<Derived>());

请注意,在处理 unique_ptr 时,您仍然可以使用原始指针(使用 .get() 成员函数)但含义不同:我 知道 由其他人拥有 的对象(此处为 unique_ptr)。

并且如评论中所述,为了确保正确销毁,Base 的析构函数应为 virtual ~Base()=default;,因为 baseCollection 的析构函数不知道 Base 指针实际上指向一个 Derived (可能大小不一样,需要动态调度)。这与 unique_ptrshared_ptr 无关;原始指针存在问题。 更进一步,由于我们有多态类型,我们应该禁用 (=delete) copy/move-constructors/assign 以防止切片,但我们离最初的问题还很远。

Is refactoring this as shown below the correct way?

重构程序的行为与第一个不同。第一个程序会泄漏内存,而重构后的程序不会。只要是故意的,这当然通常是一件好事。

I use C++17. std::unique_ptr instead of std::shared_ptr won't compile in this case.

那是因为您复制了指针,而唯一指针是不可复制的。您可以改为移动指针。但是,指向基的唯一指针还有一个共享指针没有的问题:如果通过指向基的指针删除派生对象,并且基的析构函数不是虚拟的,那么程序的行为将是未定义的。这可以通过将基础的析构函数设为虚拟来轻松解决。

因此,只要不复制指针,并且将析构函数设为虚拟,就无需支付共享所有权的开销。