Можно ли приводить контейнер STL с базовым типом к производному типу? - PullRequest
5 голосов
/ 28 марта 2011

Можно ли преобразовывать контейнер STL из базового типа в производный?Например, у меня есть два вектора.Первый относится к типу базового класса, второй - к типу производного класса.

class Base
{
// Code
};

class Derive : public Base
{
// Code
};

Использование

    vector<Base*>*  vec_base = new vector<Base*>;

    // Add some Derive type data to vec_base

    vector<Derive*>* vec_derive = (vector<Derive*>*)(vec_base);

    // Using elements as Derive pointers. Works fine. 

Это нормально?(Это отлично работает, но я хотел получить некоторые комментарии по этому поводу).Большое спасибо.

РЕДАКТИРОВАТЬ: Обновление в соответствии с ответами.

Скажите, если я буду осторожно использовать этот вектор, не буду использовать его с множественным наследованием и не буду вставлять объекты, отличные от типа Derive, это нормально?(Наверное, это не так)

И большое спасибо за ответы.

Ответы [ 5 ]

9 голосов
/ 28 марта 2011

Это определенно не нормально и является одним из примеров ошибок маскирования приведения в стиле c. «Это работает для меня» не указывает на четко определенное поведение в данном случае.

Если вы действительно хотите это сделать, я бы предложил:

#include <vector>
#include <algorithm>
#include <iterator>

using namespace std;

class Base
{
// Code
virtual ~Base();
};

class Derrive : public Base
{
// Code
};

Derrive *convert(Base * in) {
   // assert here?
   return dynamic_cast<Derrive*>(in);
}

int main() {
    vector<Base*>*  vec_base = new vector<Base*>;

    // Add some Derrive type data to vec_base

    vector<Derrive*>* vec_derrive = new vector<Derrive*>;

    transform(vec_base->begin(), vec_base->end(), back_insert_iterator<vector<Derrive*> >(*vec_derrive), convert);
}
2 голосов
/ 28 марта 2011

Вы выполняете приведение в стиле c, которое, по сути, выполняет reinterpret_cast, который говорит компилятору "обрабатывать x как y с этого момента, и просто поверьте мне, что он работает". Так что он обязательно скомпилируется, но это плохая идея. Здесь нет никакой безопасности типов, и она может работать некоторое время, но в другое время она будет непредсказуемым.

Что вы можете сделать вместо этого:

for (unsigned int i=0; i < vec_base->length(); i++)
{
  Derrive* d = dynamic_cast<Derrive*> (vec_base[i]);
  if (d ) {
     // this element is a Derrive instance, so we can treat it like one here
  }
  // else, skip it, log an error, throw an exception, whatever,
  // this element in the vector is not of type Derrive
}
1 голос
/ 28 марта 2011

Это не нормально. Шаблонные типы с различными T являются несвязанными типами (даже если они оба говорят std::vector, а использование приведения в стиле C просто позволяет вам избежать неопределенного поведения.

Если это пока работает, считайте, что вам не повезло, что он не рухнул.

Если вы знаете, что ВСЕ элементы в векторе являются производным классом, тогда просто сделайте так, чтобы вектор указывал на производные объекты заранее. Если вы этого не знаете, актерский состав небезопасен.

0 голосов
/ 05 октября 2011

Более безопасный способ сделать это - использовать std :: transform.

Но, поскольку внутри реализации std :: list T = Base * или T = Derived * ведет себя одинаково (оба имеют одинаковый размер), список имеет ту же внутреннюю структуру, что и список. Так что можно сделать следующий трюк:

vector<Derived*> vector2 = *( reinterpret_cast< vector<Derived*>* >(&vector1) );

Примечание: Мой ответ носит информационный характер, пожалуйста, придерживайтесь подхода std :: transform. Я не уверен, что в реализации STDC ++, кроме GCC, вектор ведет себя так же, т.е. Я не уверен, что утверждение "list и list имеют одинаковую внутреннюю структуру" верно в других реализациях.

0 голосов
/ 28 марта 2011

Нет.Это не сработает.

Скажем, у меня есть Derrive2, полученное из Base.Я могу положить это в контейнер STL, но он не будет безопасно приведен к Derrive.

...