Создание и удаление массива виртуальных объектов
От: Михаил Лёсин Россия  
Дата: 29.07.10 13:18
Оценка:
Никак не могу понять, что в этом коде сделано неверно:
#include <iostream>
#include <string>

using std::string;
using std::cout;
using std::endl;

class A
{
    string name;
public:
    A(const char *title):
        name(title)
    {
        cout<<"A::A for '"<<name<<"'"<<endl;
    }
    ~A()
    {
        cout<<"A::~A for '"<<name<<"'"<<endl;
    }
};

class B
{
    A ba;
public:
    B():ba("b::a"){}
    virtual ~B(){}
    virtual void f(){}
};

class C : public B
{
    A ca;
public:
    C():ca("c::a"){}
};

int main()
{
    B *ptr = new C[5];
    delete[] ptr;
    return 0;
}


На винде в MSVC вроде работает, а в GCC на линуксе и маке вижу следующее поведение:
A::A for 'b::a'
A::A for 'c::a'
A::A for 'b::a'
A::A for 'c::a'
A::A for 'b::a'
A::A for 'c::a'
A::A for 'b::a'
A::A for 'c::a'
A::A for 'b::a'
A::A for 'c::a'
Segmentation fault


Что я делаю не так?
WBW, Mike.
Re: Создание и удаление массива виртуальных объектов
От: Guard_h4s Россия  
Дата: 29.07.10 13:43
Оценка: 6 (1) +2 -1
Здравствуйте, Михаил Лёсин, Вы писали:

МЛ>Что я делаю не так?

Уже который раз)
Нельзя рассматривать массивы полиморфно. Они для этого не предназначены
Re: Создание и удаление массива виртуальных объектов
От: okman Беларусь https://searchinform.ru/
Дата: 29.07.10 14:48
Оценка: +1 -1
Здравствуйте, Михаил Лёсин, Вы писали:

...

МЛ>Что я делаю не так?


Вы адресуете массив элементов одного типа (C) указателем на другой тип (B), а происходит это
при удалении массива (delete [] ptr). Операция delete в данном случае логически эквивалентна
обходу всех элементов и вызову их деструкторов через указатель на B. А поскольку размеры
типов B и C различны, различается и арифметика соответствующих указателей — B имеет другой размер "шага".
То, что типы связаны наследованием, к делу отношения не имеет.

Та же ситуация возникает, если попытаться вызвать метод f() одного из элементов (кроме первого):

int main()
{
    B *ptr = new C[5];

    ptr[3].f(); // << та же ошибка

    delete[] ptr;
    return 0;
}


Видимо, компилятор от MS где-то сохраняет тип элементов массива, поэтому приведенный Вами код
"прокатывает", хотя и не должен, на мой взгляд.
Re: Создание и удаление массива виртуальных объектов
От: Vamp Россия  
Дата: 29.07.10 17:01
Оценка:
МЛ>Что я делаю не так?
Как уже писали выше (непонятно, почему минусы поставили) массивы нельзя трактовать полиморфно.
В твоей ситуации тебе надо создавать массив указателей на объекты, а не массив объектов. А еще лучше, вектор. Примерно так:


vector<B*> vec;

vec.reserve(SIZE);

for (int i = 0; i < SIZE; ++i)
     vec.push_back(new C);

 
....

for (vec<B*>::iterator b = vec.begin(), e = vec.end(), b! = e; ++b)
     delete(*b);
}
Да здравствует мыло душистое и веревка пушистая.
Re: Создание и удаление массива виртуальных объектов
От: pzhy  
Дата: 29.07.10 17:16
Оценка:
Здравствуйте, Михаил Лёсин, Вы писали:

МЛ>Никак не могу понять, что в этом коде сделано неверно:


МЛ>Что я делаю не так?


К сожалению надо так:

#include <iostream>
#include <string>

using std::string;
using std::cout;
using std::endl;

class A
{
string name;
public:
A(const char *title):
name(title)
{
cout<<"A::A for '"<<name<<"'"<<endl;
}
~A()
{
cout<<"A::~A for '"<<name<<"'"<<endl;
}
};

class B
{
A ba;
public:
B():ba("b::a"){}
virtual ~B(){ cout<<"b::~A for '"<<"'"<<endl;}
virtual void f(){}
};

class C : public B
{
A ca;
public:
C():ca("c::a"){}
virtual ~C()
{
cout<<"c::~A for '"<<"'"<<endl;
}
};

int main()
{
B* ptr[5];
ptr[0]= new C;
ptr[1]= new C;
ptr[2]= new C;
ptr[3]= new C;
ptr[4]= new C;

delete ptr[0];
delete ptr[1];
delete ptr[2];
delete ptr[3];
delete ptr[4];
return 0;
}

Но лучше так:
#include <iostream>
#include <string>
#include <list>
#include <boost/scoped_array.hpp>

using namespace boost;
using namespace std;

class A
{
string name;
public:
A(const char *title):
name(title)
{
cout<<"A::A for '"<<name<<"'"<<endl;
}
~A()
{
cout<<"A::~A for '"<<name<<"'"<<endl;
}
};

class B
{
A ba;
public:
B():ba("b::a"){}
virtual ~B(){ cout<<"b::~A for '"<<"'"<<endl;}
virtual void f(){}
};

class C : public B
{
A ca;
public:
C():ca("c::a"){}
virtual ~C()
{
cout<<"c::~A for '"<<"'"<<endl;
}
};

int main()
{
scoped_array<B> b(new B[5]);
return 0;
}
Re[2]: Создание и удаление массива виртуальных объектов
От: MasterZiv СССР  
Дата: 29.07.10 20:03
Оценка:
okman wrote:

> Видимо, компилятор от MS где-то сохраняет тип элементов массива, поэтому

> приведенный Вами код
> "прокатывает", хотя и не должен, на мой взгляд.

Именно так, об этой фиче немало топиков написано.
Posted via RSDN NNTP Server 2.1 beta
Re[2]: Создание и удаление массива виртуальных объектов
От: -MyXa- Россия  
Дата: 30.07.10 03:25
Оценка:
Здравствуйте, Vamp, Вы писали:

V>
V>vector<B*> vec;

V>vec.reserve(SIZE);

V>for (int i = 0; i < SIZE; ++i)
V>     vec.push_back(new C);
 
V>


Ну, прямо так, когда vec — локальная переменная, конечно, нельзя — любая неприятность, в виде исключения, в push_back, операторе new, или, не дай Бог, в конструкторе С, и будет утечка памяти.

Майерс даже не смог пройти мимо этой темы — так и говорил, дескать, "never treat arrays polymorphically" и баста.
Если не поможет, будем действовать током... 600 Вольт (C)
Re: Создание и удаление массива виртуальных объектов
От: Ytz https://github.com/mtrempoltsev
Дата: 30.07.10 05:42
Оценка: 1 (1)
Здравствуйте, Михаил Лёсин, Вы писали:

МЛ>Что я делаю не так?


Посоветую как сделать так — использовать Boost:

boost::ptr_array<B, 5> ptr;
for (int i = 0; i != 5; ++i)
    ptr.replace(i, new C());


Заодно не надо помнить про delete. Я вообще всегда настораживаюсь когда вижу в коде delete
Re[2]: Создание и удаление массива виртуальных объектов
От: okman Беларусь https://searchinform.ru/
Дата: 30.07.10 06:24
Оценка: +1
Здравствуйте, Ytz, Вы писали:

...

Ytz>Заодно не надо помнить про delete. Я вообще всегда настораживаюсь когда вижу в коде delete


А я настораживаюсь, когда вижу new и не вижу delete...
Re[3]: Создание и удаление массива виртуальных объектов
От: Ytz https://github.com/mtrempoltsev
Дата: 30.07.10 06:26
Оценка: +1
Здравствуйте, okman, Вы писали:

O>Здравствуйте, Ytz, Вы писали:


O>...


Ytz>>Заодно не надо помнить про delete. Я вообще всегда настораживаюсь когда вижу в коде delete


O>А я настораживаюсь, когда вижу new и не вижу delete...


Могу только посочувствовать — ловить утечки памяти дело тяжелое, но неблагодарное.
Re[3]: Создание и удаление массива виртуальных объектов
От: Кодт Россия  
Дата: 31.07.10 08:52
Оценка:
Здравствуйте, -MyXa-, Вы писали:

MX>Майерс даже не смог пройти мимо этой темы — так и говорил, дескать, "never treat arrays polymorphically" и баста.


Чисто в порядке дуракаваляния: можно сделать вот такое
template<class T>
class poly_array
{
private:
  shared_ptr<void> m_data;
  size_t m_elem_size;

public:
  poly_array() {}

  template<class V>
  poly_array( V* arr
            , void(*del)(V*) = checked_array_delete<V>              // запоминаем delete[] (V*)arr
            , enable_if< is_base_and_derived<T,V>, void*>::type = 0 // чтобы не подсовывали всякую фигню
            )
    : m_data(arr, del)
    , m_elem_size(sizeof(V))
  {}

  operator unspecified_bool_type() const { return m_data; }

  // доступа к массиву как таковому нет, а к элементам - есть
  T& operator[](size_t i) const // глубокая или поверхностная константность - дело вкуса
  {
    assert(m_data);
    return *(T*)((char*)(m_data.get()) + m_elem_size*i); // адресная арифметика руками
  }
};
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.