Как неинтрузивные умные указатели ведут себя в отношении наследования и множественного наследования? - PullRequest
5 голосов
/ 13 октября 2010

Я использую C ++.C ++ 0x, используя Visual Studio 2010, чтобы быть корректным.

Предположим, у меня есть класс Z. Чтобы в моем приложении было безопаснее работать с указателями на этот класс, я могу последовательно использовать умные указатели (общий указатель,слабый указатель).

Теперь этот класс Z наследуется от класса X. Некоторые части моего приложения будут работать с указателями на класс X, другие будут работать с указателями на класс Z.

  • Могу ли я по-прежнему использовать умные указатели?
  • Работают ли общие указатели, если у меня есть некоторые, которые ссылаются на X, а другие - на Z?Гарантируется ли, что уничтожение последнего общего указателя на экземпляр (независимо от того, является ли он std::shared_ptr<X> или std::shared_ptr<Z>) удаляет экземпляр?Уверен ли я, что если я удалю std::shared_ptr<X>, этот экземпляр будет храниться до тех пор, пока существует еще один std::shared_ptr<Y>?

Теперь предположим, что я использую множественное наследование, где Z наследуется от классов X иY. Некоторые части моего приложения будут работать с std::shared_ptr<X>, другие с std::shared_ptr<Y> и другие с std::shared_ptr<Z>.

  • Могу ли я по-прежнему использовать общие указатели таким образом?
  • Все еще гарантируется, что только последний умный указатель (независимо от того, указывает ли он на X, Y или Z) удаляет экземпляр?

Кстати, как я могу безопасно привести один умный указатель к другому?Например, приведение std::shared_ptr<Z> к std::shared_ptr<X>?Это работает?Это разрешено?

Обратите внимание, что я явно ссылаюсь на неинтрузивные указатели (как новые std::shared_ptr и std::weak_ptr в C ++ 0x).При использовании навязчивых указателей (как в Boost), это, вероятно, работает, поскольку сам экземпляр отвечает за сохранение счетчика.

1 Ответ

6 голосов
/ 13 октября 2010

Да, это поддерживается стандартом, §20.9.11.2.10 [util.smartptr.shared.cast].

Необходимые вам утилиты:

  • std::static_pointer_cast<>()
  • std::dynamic_pointer_cast<>()

Они имеют ту же семантику, что и их счетные части C ++ 03 static_cast<>() и dynamic_cast<>().Единственное отличие состоит в том, что они работают только на std::shared_ptr с.И просто для многословия, они делают то, что вы ожидаете, и правильно делят счетчик ссылок между исходным и вновь приведенным shared_ptr с.

struct X { virtual ~X(){} };
struct Y : public X {};
struct Z : public X {};

int main()
{
   {
      //C++03
      X* x = new Z;
      Z* z = dynamic_cast<Z*>(x);
      assert(z);
      x = new Y;
      z = dynamic_cast<Z*>(x);
      assert(!z);
      z = static_cast<Z*>(x);
      assert(z); //EVIL!!!
   }

   {
      //C++0x
      std::shared_ptr<X> x{new Z};
      std::shared_ptr<Z> z{std::dynamic_pointer_cast<Z>(x)};
      assert(z);
      x = std::make_shared<Y>();
      z = std::dynamic_pointer_cast<Z>(x);
      assert(!z);
      z = std::static_pointer_cast<Z>(x);
      assert(z); //EVIL!!!

      // reference counts work as expected.
      std::shared_ptr<Y> y{std::static_pointer_cast<Y>(x)};
      assert(y);

      std::weak_ptr<Y> w{y};
      assert(w.expired());

      y.reset();
      assert(w.expired());

      x.reset();
      assert(!w.expired());      
   }
   {
      //s'more nice shared_ptr features
      auto z = std::make_shared<X>();
      std::shared_ptr<X> x{z};
      assert( z == x );
      x = z; //assignment also works.
   }
}
...