Приведите auto_ptr <Base>к auto_ptr <Derived> - PullRequest
0 голосов
/ 20 декабря 2011

Пожалуйста, помогите мне разобраться в следующей проблеме.

Посмотрите на приведенный ниже пример кода:

#include <iostream>

class Shape {
public:
  virtual wchar_t *GetName() { return L"Shape"; }
};
class Circle: public Shape {
public:
  wchar_t *GetName() { return L"Circle"; }
  double GetRadius() { return 100.; }
};

int wmain() {
  using namespace std;

  auto_ptr<Shape> aS;
  auto_ptr<Circle> aC(new Circle);

  aS = aC;
  wcout << aS->GetName() << L'\t' << static_cast<auto_ptr<Circle>>(aS)->GetRadius() << endl;

  return 0;
}

Почему мне не разрешено делать это:

static_cast<auto_ptr<Circle>>(aS)->GetRadius()
*Компилятор 1008 * (MSVCPP 11):
1>c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory(911): error C2440: 'initializing' : cannot convert from 'Shape *' to 'Circle *'
1>          Cast from base to derived requires dynamic_cast or static_cast

Ответы [ 2 ]

5 голосов
/ 20 декабря 2011

auto_ptr не ведет себя так же, как указатель в этом отношении. В языке существуют специальные правила, позволяющие Shape* быть static_cast до Circle*, когда Circle происходит от Shape. Понижение не является полностью безопасным для типов, так как он полагается на пользователя, чтобы предоставить значение указателя, которое фактически указывает на подобъект базового класса Shape в Circle, но стандарт допускает это для удобства. auto_ptr "просто" класс библиотеки и не имеет эквивалентного преобразования.

Даже если бы вы могли это сделать, это часто давало бы сбои. При копировании auto_ptr оригинал теряет право собственности на ресурс. Ваш static_cast будет скопировать auto_ptr во временное состояние, и поэтому aS будет сброшен, и ресурс будет уничтожен, когда временное значение равно (в конце выражения). В вашем примере это нормально, так как он все равно будет уничтожен на return, но, вообще говоря, вы не хотите копировать auto_ptr кроме как в параметре вызова функции или возвращаемом значении, чтобы указать передачу владения от вызывающего к вызываемому. или наоборот.

Вместо этого вы можете static_cast<Circle*>(aS.get())->GetRadius(), или, что еще лучше, реструктурировать свой код, чтобы избежать необходимости снижения производительности. Если вы знаете, что ваш объект Circle, сохраните его в auto_ptr<Circle> [*]. Если вы держите его в auto_ptr<Shape>, то не полагайтесь на то, что он Circle.

[*] Или, если ваша реализация предоставляет их, лучший умный указатель, такой как unique_ptr, scoped_ptr или shared_ptr. Даже если ваша реализация их не предоставляет, есть Boost.

3 голосов
/ 20 декабря 2011

Вы, конечно, не хотите выполнять это приведение, поскольку std::auto_ptr<T> становится владельцем внутреннего указателя при инициализации с другим экземпляром класса.

aS, следовательно, потеряет указатель, и ваш new Circle объект будет уничтожен в конце оператора std::cout, так как указатель объекта теперь принадлежит временному.

Вместо этого вы, вероятно, ищете что-то вроде следующего:

cout << ... << static_cast<Circle*>(aS.get ())->GetRadius() << endl;

Вы также можете привести его к ссылке, как показано ниже:

cout << ... << static_cast<Circle&> (*aS).GetRadius () << endl;
...