Кому не лень ковыряться в чужом коде
От: Idler Россия  
Дата: 18.07.02 07:18
Оценка:
//------------------------------------------------------------------------------
// Мечта моего детства —
// создать "динамический массив"
// такой, чтобы не следить за памятью, ни при создании, ни при удалении,
// и снаружи никаких указателей.

// В конце файла — реализация этого класса, и соответствующие жалобы
//------------------------------------------------------------------------------
template <class T> class IList{
public:
int Length;
T *Items; //Указатель на сам массив

IList();
~IList();
IList operator = (IList);
T& operator [](int i) {return Items[i];}
void Add(T); //Добавить элемент к массиву
void Add(); //Добавить пустой элемент к массиву
void Clear(); //Отчискта массива
private:
};
//-------------------------------------------------------------------------------------
template <class T> IList<T>::IList(){
Length = 0;
}
//-------------------------------------------------------------------------------------
template <class T> IList<T>::~IList(){
delete Items;
}
//-------------------------------------------------------------------------------------
template <class T> IList<T> IList<T>::operator = (IList<T> data){
delete Items; //Удалить старый список
Length = 0;
for(int i=0; i<data.Length; i++)
Add(data.Items[i]); //Создать новый
return *this;
}
//-------------------------------------------------------------------------------------
template <class T> void IList<T>::Add(T data){
T *NewItems = new T[Length+1]; //Создать новый список (длинее существующего на 1)
for(int i = 0; i < Length; i++)
NewItems[i] = Items[i]; //Копировать старые данные в новые
NewItems[Length] = data; //Записать новые данные в конец нового списка
delete Items; //Удалить старый список
Items = NewItems; //Сохранить адресс нового списка
Length++; //Увеличить длинну списка на 1
}
//-------------------------------------------------------------------------------------
template <class T> void IList<T>::Add(){
T *NewItems = new T[Length+1];
for(int i = 0; i < Length; i++)
NewItems[i] = Items[i];
delete Items;
Items = NewItems;
Length++;
}
//-------------------------------------------------------------------------------------
template <class T> void IList<T>::Clear(){
delete Items;
Length = 0;
Items = 0;
}
//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender){
int i, j, c = 2;

IList <int> a;
for(i=0; i<c; i++)
a.Add(i);
for(i=0; i<a.Length; i++)
Memo1->Lines->Add(IntToStr(a[i]));
//До сюда работает, но если закрыть программу — ругается


//А дальше ругается сразу во время выполнения
IList <IList <int> > b; //двумерный массив
for(i=0; i<c; i++){
b.Add();
for(j=0; j<c; j++)
b[i].Add(j);
}
for(i=0; i<b.Length; i++)
for(j=0; j<b[i].Length; j++)
Memo1->Lines->Add(IntToStr( b[i][j] ));
}
//-------------------------------------------------------------------------------------
// Где-то, где я не понял, вызывается деструктор.
// Почему он вызывается?
// Еще вопрос — Если удалять динамический массив, будет ли вызываться
// деструктор для каждого его элемента?
//-------------------------------------------------------------------------------------
Re: Кому не лень ковыряться в чужом коде
От: SergH Россия  
Дата: 18.07.02 07:23
Оценка:
Здравствуйте Idler, Вы писали:

I>//------------------------------------------------------------------------------

I>// Мечта моего детства —
I>// создать "динамический массив"
I>// такой, чтобы не следить за памятью, ни при создании, ни при удалении,
I>// и снаружи никаких указателей.

Используй vector из STL

I>template <class T> IList<T>::~IList(){

I>delete Items;
I>}

Нужно delete[] Items;

[scip]

I>//-------------------------------------------------------------------------------------

I>// Где-то, где я не понял, вызывается деструктор.
I>// Почему он вызывается?

Не понял. Просто так деструкторы не вызываются.

I>// Еще вопрос — Если удалять динамический массив, будет ли вызываться

I>// деструктор для каждого его элемента?

Если сделать так, как сделал ты — нет. Если так, как посоветовал я — да.
Делай что должно, и будь что будет
Re[2]: Кому не лень ковыряться в чужом коде
От: Idler Россия  
Дата: 18.07.02 07:34
Оценка:
Здравствуйте SergH, Вы писали:

SH>Используй vector из STL

STL — что это? (я не много чайник)

[scip] — это что?

SH>Не понял. Просто так деструкторы не вызываются.


Items = new MyClass[256];
а в MyClass, допустим, выделялась память, и она удаляется деструктором
Re[3]: Кому не лень ковыряться в чужом коде
От: SergH Россия  
Дата: 18.07.02 07:44
Оценка:
Здравствуйте Idler, Вы писали:

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


SH>>Используй vector из STL

I>STL — что это? (я не много чайник)

Standard Template Library — стандартная C++-ная библиотека шаблонов, поддерживающая векторы, списки и ещё много полезных вещей. На rsdn есть статья про STL.

I>[scip] — это что?


Это искажённое skip — пропуск, обход, скачёк.

SH>>Не понял. Просто так деструкторы не вызываются.


I>Items = new MyClass[256];

I>а в MyClass, допустим, выделялась память, и она удаляется деструктором

У тебя было написано: "Где-то, где я не понял, вызывается деструктор. Почему он вызывается?"

Для автоматических объектов (в стеке) деструктор вызывается когда они выходят из области видимости.
Для динамических объектов (в куче) — когда выполняется delete.
Для статических (глобальные или static) — когда завершается программа.

Насколько я знаю, больше нигде деструктор вызываться не может. Поэтому вопроса я не понимаю, и твоё пояснение мне не помогло.
Делай что должно, и будь что будет
Re: Кому не лень ковыряться в чужом коде
От: Vi2 Удмуртия http://www.adem.ru
Дата: 18.07.02 09:01
Оценка: 2 (1)
Здравствуйте Idler, Вы писали:
I>//------------------------------------------------------------------------------
I>//    Мечта моего детства - 
I>//    создать "динамический массив"
I>//    такой, чтобы не следить за памятью, ни при создании, ни при удалении,
I>//    и снаружи никаких указателей.

I>//    В конце файла - реализация этого класса, и соответствующие жалобы
I>//------------------------------------------------------------------------------
I>template <class T> class IList{
I>    public:
I>    int Length;
I>    T *Items;            //Указатель на сам массив

I>    IList();
I>    ~IList();
I>    IList operator = (IList); // *)
I>    T& operator [](int i) {return Items[i];}
I>    void Add(T);            //Добавить элемент к массиву // *)
I>    void Add();            //Добавить пустой элемент к массиву
I>    void Clear();            //Отчискта массива
I>    private:
I>    };
I>//-------------------------------------------------------------------------------------
I>template <class T> IList<T>::IList(){
I>    Length = 0;
    Items = 0;
I>    }

В конструкторе ВСЕГДА нужно проинициализировать ВСЕ используемые на чтение члены. Так здесь Items не проинициализирован, сдеовательно, все действия с ним в дальнейшем могут привести к краху. Дополнительно, такие классы (с внутренним выделением памяти) ТРЕБУЮТ наличия конструктора копирования (имеющим сигнатуру в твоём случае IList( const IList& ) ) для корректного копирования выделенной памяти.
I>//-------------------------------------------------------------------------------------
I>template <class T> IList<T>::~IList(){
I>    delete [] Items;
I>    }

В таком операторе delete и будут вызываться деструкторы класса T, если они есть.
I>//-------------------------------------------------------------------------------------
I>template <class T> IList<T> IList<T>::operator = (IList<T> data){
I>    delete [] Items;                //Удалить старый список
I>    Length = 0;
I>    for(int i=0; i<data.Length; i++)
I>        Add(data.Items[i]);        //Создать новый
I>    return *this;
I>    }

Как правило, IList operator=(IList) — грубая ошибка (или неэффективная реализация) оператора присваивания, потому как будут вызваны 2 конструктора копирования класса IList — при передаче параметра и при выходе из оператора. Это тебе надо? Лучше IList& operator=(const IList&) без конструкторов копирования.
I>//-------------------------------------------------------------------------------------
I>template <class T> void  IList<T>::Add(T data){
I>    T *NewItems = new T[Length+1];        //Создать новый список (длинее существующего на 1)
I>    for(int i = 0; i < Length; i++)
I>        NewItems[i] = Items[i];        //Копировать старые данные в новые
I>    NewItems[Length] = data;        //Записать новые данные в конец нового списка
I>    delete [] Items;            //Удалить старый список
I>    Items = NewItems;            //Сохранить адресс нового списка
I>    Length++;                //Увеличить длинну списка на 1
I>    }

Здесь та же проблема, что и с оператором присваивания. Лучше Add(const T& data) без конструктора копирования.
I>//-------------------------------------------------------------------------------------
I>template <class T> void  IList<T>::Add(){
I>    T *NewItems = new T[Length+1];
I>    for(int i = 0; i < Length; i++)
I>        NewItems[i] = Items[i];
I>    delete [] Items;
I>    Items = NewItems;
I>    Length++;
I>    }
I>//-------------------------------------------------------------------------------------
I>template <class T> void IList<T>::Clear(){
I>    delete [] Items;
I>    Length = 0;
I>    Items = 0;
I>    }
I>//-------------------------------------------------------------------------------------
I>//-------------------------------------------------------------------------------------
I>void __fastcall TForm1::FormCreate(TObject *Sender){
I>    int i, j, c = 2;

I>    IList <int> a;
I>    for(i=0; i<c; i++)
I>        a.Add(i);
I>    for(i=0; i<a.Length; i++)
I>        Memo1->Lines->Add(IntToStr(a[i]));
I>    //До сюда работает, но если закрыть программу - ругается


I>    //А дальше ругается сразу во время выполнения
I>    IList <IList <int> > b; //двумерный массив
I>    for(i=0; i<c; i++){
I>        b.Add();
I>        for(j=0; j<c; j++)
I>            b[i].Add(j);
I>        }
I>    for(i=0; i<b.Length; i++)
I>        for(j=0; j<b[i].Length; j++)
I>            Memo1->Lines->Add(IntToStr( b[i][j] ));
I>    }


Есть много кода, написанного хорошими программистами и не очень, по спискам, динамическим массивам — в MFC, ATL, STL.
Посмотри как реализуются такие классы в этих средствах и на их основе тренируйся.

Я знаю такие классы в ATL —
CComDynamicUnkArray в atl\include\atlcom.h
CSimpleArray, CSimpleValArray, CSimpleMap в atl\include\atlbase.h
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re: Кому не лень ковыряться в чужом коде
От: Al-Ko  
Дата: 18.07.02 09:53
Оценка:
Здравствуйте Idler, Вы писали:
....
SKIPPED

Для таких случаев сделал небольшую резекцию MFC-шному CArray:


template<class TYPE>
class AKVector 
{
public:
    AKVector(long numElements);
    AKVector();
    virtual ~AKVector();
    long GetSize() const;
    void Insert(long nIndex, long nCount = 1);
    void Append(long nCount = 1);
    void Remove(long nIndex, long nCount = 1);
    TYPE& operator[](long nIndex);
private:
    TYPE* m_pData;   // the actual array of data
    long m_nSize;     // # of elements 

};


template<class TYPE>
inline long AKVector<TYPE>::GetSize() const
    { return m_nSize; }


template<class TYPE>
inline TYPE& AKVector<TYPE>::operator[](long nIndex)
    { return m_pData[nIndex]; }



template<class TYPE>
AKVector<TYPE>::AKVector(long numElements )
    {
        m_pData = NULL;
        m_nSize = 0;
    
        Append(numElements);
        return;

    }
template<class TYPE>
AKVector<TYPE>::AKVector()
    {
        m_pData = NULL;
        m_nSize = 0;
        return;

    }

template<class TYPE>
AKVector<TYPE>::~AKVector()
    {
        delete [] m_pData;
        m_nSize = 0;
        return;

    }


template<class TYPE>
void AKVector<TYPE>::Insert(long nIndex, long nCount /* = 1 */)
    {
        ATLASSERT(nIndex < m_nSize);
    
        long nNewSize = m_nSize + nCount;
        TYPE* pNewData =  new TYPE[nNewSize];

        if(nIndex > 0)
            memmove(pNewData, m_pData, nIndex * sizeof(TYPE));

        memmove(&pNewData[nIndex + nCount], &m_pData[nIndex], (m_nSize-nIndex)* sizeof(TYPE));
        
        delete [] m_pData;

        m_nSize = nNewSize;
        m_pData = pNewData;

    }

template<class TYPE>
void AKVector<TYPE>::Append(long nCount /* = 1 */)
    {
        long nNewSize = m_nSize + nCount;
        
        TYPE* pNewData =  new TYPE[nNewSize];

        if(m_nSize > 0){
            memmove(pNewData, m_pData, m_nSize * sizeof(TYPE));
            delete [] m_pData;
        }

        m_nSize = nNewSize;

        m_pData = pNewData;

    }


template<class TYPE>
void AKVector<TYPE>::Remove(long nIndex, long nCount /* = 1 */)
    {
        ATLASSERT(nIndex >= 0);
        ATLASSERT(nCount >= 0);
    
        ATLASSERT(nIndex + nCount <= m_nSize);
    
        long nNewSize = m_nSize - nCount;

        TYPE* pNewData;

        if(nNewSize > 0)
            pNewData =  new TYPE[nNewSize];
        else
            pNewData = NULL;

        if(nIndex > 0)
            memmove(pNewData, m_pData, nIndex * sizeof(TYPE));

        if(nIndex < nNewSize)
            memmove(&pNewData[nIndex ], &m_pData[nIndex + nCount], (m_nSize-nIndex-nCount)* sizeof(TYPE));

        delete [] m_pData;

        m_nSize = nNewSize;
        m_pData = pNewData;

    }



получился облегченный динамический Array. С его помощью, кстати, можно делать и двумерные массивы:


    typedef AKVector <TCHAR*> vecClipRows;
    AKVector <vecClipRows> ClipArray;
        ClipArray.Append(NUMROWS);
        for (int i = 0; i< NUMROWS;i++){
              ClipArray[i].Append(NUMCOLS);
              for(int j = 0; j<NUMCOLS;j++){
                    ClipArray[i][j] = new TCHAR [nTextLen];
              }
        }


причем количество столбцов в каждой строке может быть разным.

перед выходом из области видимости ClipArray нужно удалить все элементы (delete ClipArray [i][j] в цикле)
Старый глюк лучше новых двух!
Re[3]: Кому не лень ковыряться в чужом коде
От: rh2000  
Дата: 18.07.02 10:00
Оценка:
Здравствуйте Idler, Вы писали:

I>Items = new MyClass[256];

I>а в MyClass, допустим, выделялась память, и она удаляется деструктором

RTFM!
для MyClass должны быть определены copy constructor и operator=.
иначе при выражениях типа a=b или func(T a) будет создаваться побайтовая копия исходного объекта. если внутри класса был указатель, то у тебя появляется две копии объекта с одним и тем уже указателем. при удалении копии дергается деструктор, который освобождает память. в результате указатель в исходном объекте указывает в никуда и при попытке его заюзать все может сильно упасть.
отсюда мораль — юзай stl и определяй copy constructor и operator=.
Re[2]: Взрывоопасная смесь new[]+memmove+delete[]
От: Vi2 Удмуртия http://www.adem.ru
Дата: 18.07.02 10:20
Оценка:
Здравствуйте Al-Ko, Вы писали:

AK>template<class TYPE>
AK>void AKVector<TYPE>::Append(long nCount /* = 1 */)
AK>    {
AK>        long nNewSize = m_nSize + nCount;
AK>        
AK>        TYPE* pNewData =  new TYPE[nNewSize];
                             ---
AK>        if(m_nSize > 0){
AK>            memmove(pNewData, m_pData, m_nSize * sizeof(TYPE));
               --------
AK>            delete [] m_pData;
               -------
AK>        }

AK>        m_nSize = nNewSize;
AK>        m_pData = pNewData;
AK>    }


Взрывоопасная смесь new[]+memmove+delete[].
И как оно только у тебя работает, особенно на двумерных массивах!
Было, допустим 10 объектов TYPE в m_pData, их скопировали ПОБАЙТНО в pNewData через memmove(), удалили деструторами в delete[] m_pData и продолжаем ими пользоваться после m_pData = pNewData.

Это будет работать более-менее с классами без деструкторов, но с деструкторами, ИМХО, вылетит напрочь.

Обычно удаляют память просто через delete (void*) m_pData, чтобы не вызывать деструкторы.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[3]: Взрывоопасная смесь new[]+memmove+delete[]
От: Al-Ko  
Дата: 18.07.02 11:17
Оценка:
Здравствуйте Vi2, Вы писали:

Vi2>Здравствуйте Al-Ko, Вы писали:


Vi2>
AK>>template<class TYPE>
AK>>void AKVector<TYPE>::Append(long nCount /* = 1 */)
AK>>    {
AK>>        long nNewSize = m_nSize + nCount;
AK>>        
AK>>        TYPE* pNewData =  new TYPE[nNewSize];
Vi2>                             ---
AK>>        if(m_nSize > 0){
AK>>            memmove(pNewData, m_pData, m_nSize * sizeof(TYPE));
Vi2>               --------
AK>>            delete [] m_pData;
Vi2>               -------
AK>>        }

AK>>        m_nSize = nNewSize;
AK>>        m_pData = pNewData;
AK>>    }
Vi2>


Vi2>Взрывоопасная смесь new[]+memmove+delete[].

Vi2>И как оно только у тебя работает, особенно на двумерных массивах!
Vi2>Было, допустим 10 объектов TYPE в m_pData, их скопировали ПОБАЙТНО в pNewData через memmove(), удалили деструторами в delete[] m_pData и продолжаем ими пользоваться после m_pData = pNewData.

когда их копируют из m_pData в pNewData, они копируются в pNewData, а в m_pData остаются, или нет? Их уже два экземпляра, не так ли?Если остаются, то delete[] m_pData должно работать корректно.
Спасибо, я учту замечания и проверю с TYPE c деструкторами — с простыми типами это работает хоть с пятимерными массивами, только Redimension можно делать только последнему измерению (как в SafeArray или бейсиковских массивах)


Vi2>Это будет работать более-менее с классами без деструкторов, но с деструкторами, ИМХО, вылетит напрочь.


Vi2>Обычно удаляют память просто через delete (void*) m_pData, чтобы не вызывать деструкторы.
Старый глюк лучше новых двух!
Re[4]: Взрывоопасная смесь new[]+memmove+delete[]
От: Vi2 Удмуртия http://www.adem.ru
Дата: 18.07.02 11:39
Оценка:
Здравствуйте Al-Ko, Вы писали:

AK>когда их копируют из m_pData в pNewData, они копируются в pNewData, а в m_pData остаются, или нет? Их уже два экземпляра, не так ли?Если остаются, то delete[] m_pData должно работать корректно.

AK>Спасибо, я учту замечания и проверю с TYPE c деструкторами — с простыми типами это работает хоть с пятимерными массивами, только Redimension можно делать только последнему измерению (как в SafeArray или бейсиковских массивах)

Введу псевдо-обозначения О(ptr) — объект О типа TYPE имеет указатель на динамически выделяемую память ptr (есть аналогия с классом AKVector), чтобы было видно влияние деструктора.

m_pData->    O0(ptr0),O1(ptr1),...,ON(ptrN), где N=m_nSize

    TYPE* pNewData =  new TYPE[nNewSize];
pNewData->    O'0(NULL),O'1(NULL),...,O'N(NULL),O'N+1(NULL), где N=m_nSize

    memmove(pNewData, m_pData, m_nSize * sizeof(TYPE));
pNewData->    O0(ptr0),O1(ptr1),...,ON(ptrN),O'N+1(NULL), где N=m_nSize

    delete [] m_pData;
~O0 освободил ptr0,~O1 освободил ptr1,...,~ON освободил ptrN, где N=m_nSize
m_pData->    O0(ptr0),O1(ptr1),...,ON(ptrN), где N=m_nSize

    m_pData = pNewData;
m_pData->    O0(ptr0),O1(ptr1),...,ON(ptrN),O'N+1(NULL), где N=m_nSize

Если теперь попытаться освободить delete [] m_pData; — в деструкторе или методе Add — произойдёт следующее
~O0 будет освобождать ptr0 и вылетит по ексепшену (прерыванию).

Конечно, лучше бы представить это всё графически, но нет у меня такой возможности.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[4]: Кому не лень ковыряться в чужом коде
От: Idler Россия  
Дата: 18.07.02 15:37
Оценка:
Здравствуйте SergH, Вы писали:

template <class T> IList<T> IList<T>::operator = (IList<T> data){
delete Items; //Удалить старый список
Length = 0;
for(int i=0; i<data.Length; i++)
Add(data.Items[i]); //Создать новый
return *this;
}
Плсле return *this содержимое data изменяется, видимо для него вызывается деструктор.
Re: Кому не лень ковыряться в чужом коде
От: Аноним  
Дата: 18.07.02 15:40
Оценка:
Здравствуйте Idler, Вы писали:

I>//------------------------------------------------------------------------------

I>// Мечта моего детства —
I>// создать "динамический массив"
I>// такой, чтобы не следить за памятью, ни при создании, ни при удалении,
I>// и снаружи никаких указателей.

I>// В конце файла — реализация этого класса, и соответствующие жалобы

I>//------------------------------------------------------------------------------
I>template <class T> class IList{
I> public:
I> int Length;
I> T *Items; //Указатель на сам массив

I> IList();

I> ~IList();
I> IList operator = (IList);
I> T& operator [](int i) {return Items[i];}
I> void Add(T); //Добавить элемент к массиву
I> void Add(); //Добавить пустой элемент к массиву
I> void Clear(); //Отчискта массива
I> private:
I> };
I>//-------------------------------------------------------------------------------------
I>template <class T> IList<T>::IList(){
I> Length = 0;
I> }
I>//-------------------------------------------------------------------------------------
I>template <class T> IList<T>::~IList(){
I> delete Items;
I> }
I>//-------------------------------------------------------------------------------------
I>template <class T> IList<T> IList<T>::operator = (IList<T> data){
I> delete Items; //Удалить старый список
I> Length = 0;
I> for(int i=0; i<data.Length; i++)
I> Add(data.Items[i]); //Создать новый
I> return *this;
I> }
I>//-------------------------------------------------------------------------------------
I>template <class T> void IList<T>::Add(T data){
I> T *NewItems = new T[Length+1]; //Создать новый список (длинее существующего на 1)
I> for(int i = 0; i < Length; i++)
I> NewItems[i] = Items[i]; //Копировать старые данные в новые
I> NewItems[Length] = data; //Записать новые данные в конец нового списка
I> delete Items; //Удалить старый список
I> Items = NewItems; //Сохранить адресс нового списка
I> Length++; //Увеличить длинну списка на 1
I> }
I>//-------------------------------------------------------------------------------------
I>template <class T> void IList<T>::Add(){
I> T *NewItems = new T[Length+1];
I> for(int i = 0; i < Length; i++)
I> NewItems[i] = Items[i];
I> delete Items;
I> Items = NewItems;
I> Length++;
I> }
I>//-------------------------------------------------------------------------------------
I>template <class T> void IList<T>::Clear(){
I> delete Items;
I> Length = 0;
I> Items = 0;
I> }

При беглом осмотре показалось странным следующее:
1. Зачем Items и Length public???
Для Length лучше написать
int Size() const { return Length; }
и все.
Об Item вообще никто кроме класса знать не должен. Если же очень нужно получать указатель на буфер, то определить ф-ию типа
const T* GetPtr() const
и
T* GetPtr()
.
2. В операторе присваивания (с учетом замечаний, уже высказанных) все сломается, если присвоить a = a.
3. Метод Add() нафиг не нужен.
4. Неэффективно наращивать массив на 1 и каждый раз переаллокировать. Лучше завести буфер и наращивать его скачками ... типа 4,8,16 и т.п. как больше нравиться... :)
Неплохо бы разделять буфер массива и сам массив. Буфер --- реже переаллокировать, не уменьшать (только отдавать весь типа FreeBuffer(), или в деструкторе).
5. Неплохо бы написать
const T& operator[int i]() const;

6. В отладочных версиях в
operator[int i]()
неплохо бы проверять на соответствие границам массива (что-нибудь типа
assert( 0 <= i && i < Length );
).
7. Можно иметь разные классы массива для простых типов и для элементов с нетривиальными констр / дестр / операторами=.
Re[5]: Кому не лень ковыряться в чужом коде
От: SergH Россия  
Дата: 18.07.02 15:54
Оценка: 2 (1)
Здравствуйте Idler, Вы писали:

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


I>template <class T> IList<T> IList<T>::operator = (IList<T> data){

I> delete Items; //Удалить старый список
I> Length = 0;
I> for(int i=0; i<data.Length; i++)
I> Add(data.Items[i]); //Создать новый
I> return *this;
I> }
I>Плсле return *this содержимое data изменяется, видимо для него вызывается деструктор.

Правильная форма operator= выглядит так:

some_class& operator=(const some_class& sc)
{
    if (this == &sc)
    {
        // приравнивание самому себе
        return *this;
    }

    // выполняем присваивание
    ..
    return *this;
}


Обрати внимание на значки &. Благодаря им ни при передаче sc в operator= ни при возврате значения из operator= копирование не требуется и конструктор копирования не вызывается. И, соответственно, не вызываются деструкторы.

Если конструктор копирования написан корректно (т.е. в твоём случае нужно выделить память, скопировать туда объекты), то отсутствие & приведёт только к низкой производительности, перерасходу памяти, но никаких катастроф не будет (точнее, я не вижу для них повода).

Но если в конструкторе копирования происходит просто почленное копирование (такое будет в конструкторе копирования генерируемом компилятором), то когда вызовется деструктор для sc освободится память, на которую указывает поле Items sc (это нормально) и поле Items исходного объекта (а это плохо).
Делай что должно, и будь что будет
Re[4]: Кому не лень ковыряться в чужом коде
От: Idler Россия  
Дата: 19.07.02 05:59
Оценка:
Здравствуйте SergH
Еше один вопросик.

template <class T> void IList<T>::Add(T& data){
T* NewItems = new T[Length+1];
for(int i = 0; i < Length; i++)
NewItems[i] = Items[i];
NewItems[Length] = data;
if(Items !=0)
delete[] Items;
Items = NewItems;
Length++;
}
По завершению функции вызывается деструктор на Items — Why? (на NewItems — не вызывается)
Re[5]: Кому не лень ковыряться в чужом коде
От: SergH Россия  
Дата: 19.07.02 06:09
Оценка:
Здравствуйте Idler, Вы писали:

I>Здравствуйте SergH

I>Еше один вопросик.

I>template <class T> void IList<T>::Add(T& data){

I> T* NewItems = new T[Length+1];
I> for(int i = 0; i < Length; i++)
I> NewItems[i] = Items[i];
I> NewItems[Length] = data;
I> if(Items !=0)
I> delete[] Items;
I> Items = NewItems;
I> Length++;
I> }
I>По завершению функции вызывается деструктор на Items — Why? (на NewItems — не вызывается)

Items — член класса IList имеющий тип T*? Тогда откуда (и какой) у него деструктор, если не секрет? И вызываеться здесь он не должен.

А приведённый код похож на правильный. Единственное, при каждом добавлении увеличивать длину на 1 — очень медленно. Нужно иметь две переменные available и length, первая отражает размер выделенной памяти, а вторая используемый размер. И проверку if (Items != 0) можно убрать, так как delete[] NULL безопастно.
Делай что должно, и будь что будет
Re[6]: Кому не лень ковыряться в чужом коде
От: Idler Россия  
Дата: 19.07.02 06:27
Оценка:
Здравствуйте SergH, Вы писали:

SH>Items — член класса IList имеющий тип T*? Тогда откуда (и какой) у него деструктор, если не секрет? И вызываеться здесь он не должен.


Не конкретно на Idlers, а на this
Re[7]: Кому не лень ковыряться в чужом коде
От: SergH Россия  
Дата: 19.07.02 06:31
Оценка:
Здравствуйте Idler, Вы писали:

I>Не конкретно на Idlers, а на this


Всё равно не знаю. Вроде не должен.
Делай что должно, и будь что будет
Re[5]: Взрывоопасная смесь new[]+memmove+delete[]
От: Al-Ko  
Дата: 19.07.02 12:02
Оценка:
Здравствуйте Vi2, Вы писали:

Vi2>Здравствуйте Al-Ko, Вы писали:


AK>>когда их копируют из m_pData в pNewData, они копируются в pNewData, а в m_pData остаются, или нет? Их уже два экземпляра, не так ли?Если остаются, то delete[] m_pData должно работать корректно.

AK>>Спасибо, я учту замечания и проверю с TYPE c деструкторами — с простыми типами это работает хоть с пятимерными массивами, только Redimension можно делать только последнему измерению (как в SafeArray или бейсиковских массивах)

Vi2>Введу псевдо-обозначения О(ptr) — объект О типа TYPE имеет указатель на динамически выделяемую память ptr (есть аналогия с классом AKVector), чтобы было видно влияние деструктора.


Vi2>
m_pData->>    O0(ptr0),O1(ptr1),...,ON(ptrN), где N=m_nSize

Vi2>    TYPE* pNewData =  new TYPE[nNewSize];
pNewData->>    O'0(NULL),O'1(NULL),...,O'N(NULL),O'N+1(NULL), где N=m_nSize

Vi2>    memmove(pNewData, m_pData, m_nSize * sizeof(TYPE));
pNewData->>    O0(ptr0),O1(ptr1),...,ON(ptrN),O'N+1(NULL), где N=m_nSize

Vi2>    delete [] m_pData;
Vi2>~O0 освободил ptr0,~O1 освободил ptr1,...,~ON освободил ptrN, где N=m_nSize
m_pData->>    O0(ptr0),O1(ptr1),...,ON(ptrN), где N=m_nSize

Vi2>    m_pData = pNewData;
m_pData->>    O0(ptr0),O1(ptr1),...,ON(ptrN),O'N+1(NULL), где N=m_nSize
Vi2>

Vi2>Если теперь попытаться освободить delete [] m_pData; — в деструкторе или методе Add — произойдёт следующее
Vi2>~O0 будет освобождать ptr0 и вылетит по ексепшену (прерыванию).

Vi2>Конечно, лучше бы представить это всё графически, но нет у меня такой возможности.


да, вот так оно веселее будет:

void AKVector<TYPE>::Remove(long nIndex, long nCount /* = 1 */)
    {
  
        ATLASSERT(nIndex >= 0);
        ATLASSERT(nCount >= 0);
    
        ATLASSERT(nIndex + nCount <= m_nSize);
    
        long nNewSize = m_nSize - nCount;

        TYPE* pNewData;

        if(nNewSize > 0)
            pNewData =  (TYPE*) new BYTE[nNewSize * sizeof(TYPE)];
        else
            pNewData = NULL;

        if(nIndex > 0)
            memcpy(pNewData, m_pData, nIndex * sizeof(TYPE));

        if(nIndex < nNewSize)
            memcpy(&pNewData[nIndex], &m_pData[nIndex + nCount], (nNewSize-nIndex)* sizeof(TYPE));
        
        DestructElements<TYPE>(&m_pData[nIndex], nCount);
        delete[] (BYTE*)m_pData;

        m_nSize = nNewSize;
        m_pData = pNewData;    
    }

inline void STDAPICALLTYPE DestructElements(TYPE* pElements, int nCount)
        {
    ATLASSERT(nCount == 0 ||
        AtlIsValidAddress(pElements, nCount * sizeof(TYPE)));

    // call the destructor(s)
    for (; nCount--; pElements++)
        pElements->~TYPE();
       }


(аналогичные изменения для Add и Insert)

проверял — работает, еще раз благодарю за замечание
Старый глюк лучше новых двух!
Re[6]: Взрывоопасная смесь new[]+memmove+delete[]
От: Vi2 Удмуртия http://www.adem.ru
Дата: 19.07.02 12:20
Оценка:
Здравствуйте Al-Ko, Вы писали:

AK>да, вот так оно веселее будет:


AK>        delete[] (BYTE*)m_pData;


AK>проверял — работает, еще раз благодарю за замечание


Ты, наверное, хотел написАть?
AK>        delete (void*)m_pData; // вместо void* можно задавать любой простой тип int* и т.д.

Ежели нет, то работать не должно. По причинам, о которых я уже говорил — будут вызываться лишние деструкторы на старом содержимом m_pData.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[7]: Взрывоопасная смесь new[]+memmove+delete[]
От: Al-Ko  
Дата: 19.07.02 12:48
Оценка:
Здравствуйте Vi2, Вы писали:

Vi2>Здравствуйте Al-Ko, Вы писали:


AK>>да, вот так оно веселее будет:


Vi2>
AK>>        delete[] (BYTE*)m_pData;
Vi2>


AK>>проверял — работает, еще раз благодарю за замечание


Vi2>Ты, наверное, хотел написАть?

Vi2>
AK>>        delete (void*)m_pData; // вместо void* можно задавать любой простой тип int* и т.д.
Vi2>

Vi2>Ежели нет, то работать не должно. По причинам, о которых я уже говорил — будут вызываться лишние деструкторы на старом содержимом m_pData.


для BYTE* не будут, будут для TYPE*
Старый глюк лучше новых двух!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.