Проблема с перегрузкой бинарного оператора + в классе
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 08.11.13 13:52
Оценка:
Я создал класс myNVector для работы с векторами произвольного размера:

в заголовочном файле:
class myNVector
{
  public:
  myNVector();
  myNVector(int n);
  myNVector(int n, long double *massif);
  ~myNVector();
  myNVector& operator=(const myNVector &vect);
  friend const myNVector& operator+(const myNVector& vect1, const myNVector& vect2);
  //...
  public:
  int n; // размерность вектора
  long double *massif; // элементы вектора
}

в cpp-файле:

//...

myNVector::myNVector()
{
    n = 0;
    massif = NULL;
}

myNVector::myNVector(int n)
{
    int i;

    this->n = n;
    this->massif = new long double[n];
    for (i=0; i<n; i++)
    {
        this->massif[i] = 0.0;
    }
}

myNVector::myNVector(int n, long double *massif)
{
    int i;
    
    this->n = n;
    this->massif = new long double[n];
    for (i=0; i<n; i++)
    {
        this->massif[i] = massif[i];
    }
}

myNVector::~myNVector()
{
    if (massif != NULL)
    {
        delete []massif;
        massif = NULL;
    }
    n = 0;
}

myNVector& myNVector::operator=(const myNVector &vect)
{
    int i;
    long double* p;

    if (this->massif != NULL)
    {
        p = this->massif;
        delete []p;
        this->massif = NULL;
    }
    this->n = vect.n;
    this->massif = new long double[n]; 
    memmove(this->massif, vect.massif, this->n*sizeof(long double));
    return *this;
}

const myNVector& operator+(const myNVector& vect1, const myNVector& vect2)
{
    int i;
    myNVector  resvect;

    resvect.n = vect1.n;
    resvect.massif = new long double[resvect.n];

    for (i=0; i<vect1.n; i++)
    {
        resvect.massif[i] = vect1.massif[i] + vect2.massif[i];
    }
    return resvect;
}

// ...


В основной программе:


myNVector AVect(2), Avect2(2), Ares(2);

Avect.massif[0] = 7.0;
Avect.massif[1] = 8.0;
Avect2.massif[0] = 9.0;
Avect2.massif[1] = 10.0;

Ares = Avect + Avect2;


И вот на этой последней строке

Ares = Avect + Avect2;


программа бьётся. Выскакивает окно с сообщением:


Debug Error!
Program: f:\Roman\Roman\testvecmat.exe
Invalid allocation size: 4294967295 bytes
(Press Retry to debug the application).


В отладчике в функции operator+(const myNVector& vect1, const myNVector& vect2) всё нормально, а потом происходит обращение к функции operator=(const myNVector &vect) и там с самого начала у переменной vect поле n равно -858993460 , а поле massif вообще не читается.

В чём причина этого? Как исправить ошибку, чтобы Ares = Avect + Avect2; нормально работало?
1613 г. = 2024 г.
Re: Проблема с перегрузкой бинарного оператора + в классе
От: saf_e  
Дата: 08.11.13 14:03
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>const myNVector& operator+(const myNVector& vect1, const myNVector& vect2)
RF>{
RF>    int i;
RF>    myNVector  resvect;

RF>    resvect.n = vect1.n;
RF>    resvect.massif = new long double[resvect.n];

RF>    for (i=0; i<vect1.n; i++)
RF>    {
RF>        resvect.massif[i] = vect1.massif[i] + vect2.massif[i];
RF>    }
RF>    return resvect;
RF>}


Вы возвращаете адрес временного объекта, оператор + в каноническом виде всегда возращает копию. Если вы напишите myNVector operator+(const myNVector& vect1, const myNVector& vect2), все будет ОК.

Это что касается вашего вопроса, а по остальному пересмотрите дизайн в пользу: std::vector<double>
Re[2]: Проблема с перегрузкой бинарного оператора + в классе
От: rg45 СССР  
Дата: 08.11.13 14:21
Оценка:
Здравствуйте, saf_e, Вы писали:

_>Вы возвращаете адрес временного объекта...


Точнее, не адрес, а ссылку на временный объект (который после завершения функции прекратил существовать и ссылка больше не действительна).
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Проблема с перегрузкой бинарного оператора + в классе
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 08.11.13 14:31
Оценка:
Здравствуйте, saf_e, Вы писали:

_>Вы возвращаете адрес временного объекта, оператор + в каноническом виде всегда возращает копию. Если вы напишите myNVector operator+(const myNVector& vect1, const myNVector& vect2), все будет ОК.


Сделал так. В результате у меня появляется другая ошибка:


Debug Assertion Failed!

Program f:\Roman\Roman\testvecmat\Debug\testvecmat.exe
File f:\dd\vctools\crt_bld\self_x86\crt\src\dbgdel.cpp
Line: 52
Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUSE)


Вот текст файла dbgdel.cpp:

/***
*dbgdel.cpp - defines C++ scalar delete routine, debug version
*
*       Copyright (c) Microsoft Corporation.  All rights reserved.
*
*Purpose:
*       Defines C++ scalar delete() routine.
*
*******************************************************************************/

#ifdef _DEBUG

#include <cruntime.h>
#include <malloc.h>
#include <mtdll.h>
#include <dbgint.h>
#include <rtcsup.h>

/***
*void operator delete() - delete a block in the debug heap
*
*Purpose:
*       Deletes any type of block.
*
*Entry:
*       void *pUserData - pointer to a (user portion) of memory block in the
*                         debug heap
*
*Return:
*       <void>
*
*******************************************************************************/

void operator delete(
        void *pUserData
        )
{
        _CrtMemBlockHeader * pHead;

        RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));

        if (pUserData == NULL)
            return;

        _mlock(_HEAP_LOCK);  /* block other threads */
        __TRY

            /* get a pointer to memory block header */
            pHead = pHdr(pUserData);

             /* verify block type */
            _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));

            _free_dbg( pUserData, pHead->nBlockUse );

        __FINALLY
            _munlock(_HEAP_LOCK);  /* release other threads */
        __END_TRY_FINALLY

        return;
}

#endif  /* _DEBUG */


52-ая строка в нём -- это _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
Что это означает? Как я понимаю, это связано с настройками с управлением памятью.
Как это исправить?
1613 г. = 2024 г.
Re[3]: Проблема с перегрузкой бинарного оператора + в классе
От: saf_e  
Дата: 08.11.13 14:55
Оценка:
Здравствуйте, RussianFellow, Вы писали:

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


_>>Вы возвращаете адрес временного объекта, оператор + в каноническом виде всегда возращает копию. Если вы напишите myNVector operator+(const myNVector& vect1, const myNVector& vect2), все будет ОК.


RF>Сделал так. В результате у меня появляется другая ошибка:


RF>Что это означает? Как я понимаю, это связано с настройками с управлением памятью.

RF>Как это исправить?

Это значит что где-то порушилась память. На первый взгляд, код правильный.
Вы нашли одну из причин, почему в с++ пропагандируют не пользоваться неуправляемой памятью Ошибки не всегда очевидны и их легко допустить.

Используйте std::vector<long double> и наступит вам счастье.
Re[4]: Проблема с перегрузкой бинарного оператора + в классе
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 08.11.13 15:35
Оценка:
Здравствуйте, saf_e, Вы писали:

_>Используйте std::vector<long double> и наступит вам счастье.


Про std::vector я знаю.

Но я бы хотел всё-таки разобраться с моим классом myNVector.
Что, если попытаться использовать глобальные переменные?
Например, так:

long double *pGlobal = NULL;
int nGlobal = 0;

//...

const myNVector& operator+(const myNVector& vect1, const myNVector& vect2)
{
    int i;
    myNVector  resvect;

    resvect.n = vect1.n;
    resvect.massif = new long double[resvect.n];

    for (i=0; i<vect1.n; i++)
    {
        resvect.massif[i] = vect1.massif[i] + vect2.massif[i];
    }
    //
    nGlobal = resvect.n;
    pGlobal = resvect.massife;
    return resvect;
}

myNVector& myNVector::operator=(const myNVector &vect)
{
    // старый код
    int i;
    long double* p;

    if (this->massif != NULL)
    {
        p = this->massif;
        delete []p;
        this->massif = NULL;
    }
    this->n = vect.n;
    this->massif = new long double[n]; 
    memmove(this->massif, vect.massif, this->n*sizeof(long double));
    // новый код
    this->n = nGlobal;
    this->massive = pGlobal;
    return *this;
}
1613 г. = 2024 г.
Re[5]: Проблема с перегрузкой бинарного оператора + в классе
От: saf_e  
Дата: 08.11.13 15:47
Оценка:
Здравствуйте, RussianFellow, Вы писали:

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


_>>Используйте std::vector<long double> и наступит вам счастье.


RF>Про std::vector я знаю.


RF>Но я бы хотел всё-таки разобраться с моим классом myNVector.


Факап был неочевиден, но предсказуем

При выходе из ф-ции временный объект будет передан наружу ч-з копирование (RVO штука опциональная), т.к. конструктора копирования у вас нет, происходит поверхностное копирование, и память расстреливается.

Если бы вы хранили буффер в std::unique_ptr, компилятор вам бы об этом намекнул.
Re[6]: Проблема с перегрузкой бинарного оператора + в классе
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 09.11.13 08:17
Оценка: :)))
Я решил проблему -- создал глобальные переменные pGlobal типа long double* и nGlobal типа int.
В функции operator+ я выделяю память для pGlobal с помощью new [] (количество элементов этого массива совпадает с размерностью вектора), nGlobal я присваиваю значение размерности вектора и с помощью функции memmove я помещаю в pGlobal элемента поля massif моего класса myNVector.
А в функции operator= я с помощью той же функции memmove я считываю данные из pGlobal в поле massif моего объекта, а полю n моего объекта я присваиваю значение nGlobal.
1613 г. = 2024 г.
Re[7]: Проблема с перегрузкой бинарного оператора + в классе
От: Vzhyk  
Дата: 09.11.13 09:18
Оценка:
09.11.2013 11:17, RussianFellow пишет:

> Я решил проблему -- создал глобальные переменные pGlobal типа long

> double* и nGlobal типа int.
> В функции operator+ я выделяю память для pGlobal с помощью new []
> (количество элементов этого массива совпадает с размерностью вектора),
> nGlobal я присваиваю значение размерности вектора и с помощью функции
> memmove я помещаю в pGlobal элемента поля massif моего класса myNVector.
> А в функции operator= я с помощью той же функции memmove я считываю
> данные из pGlobal в поле massif моего объекта, а полю n моего объекта я
> присваиваю значение nGlobal.
Бррр. Может ты лучше на С#, Java программировать будешь. Потом же
кому-то твое будет падать и он будет долго разгребаться — пожалей коллег.
Posted via RSDN NNTP Server 2.1 beta
Re[8]: Проблема с перегрузкой бинарного оператора + в классе
От: RussianFellow Россия http://russianfellow.livejournal.com
Дата: 09.11.13 10:19
Оценка: :)))
Здравствуйте, Vzhyk, Вы писали:

V>Бррр. Может ты лучше на С#, Java программировать будешь. Потом же

V>кому-то твое будет падать и он будет долго разгребаться — пожалей коллег.

Я посидел в отладчике, проверил все варианты--всё нормально у меня работает. Надо только знать, где следует подчищать память из pGlobal и nGlobal.
1613 г. = 2024 г.
Re[9]: Проблема с перегрузкой бинарного оператора + в классе
От: Vzhyk  
Дата: 09.11.13 10:41
Оценка:
09.11.2013 13:19, RussianFellow пишет:

> всё нормально у меня работает

Вот за эту фразу программистов надо сразу выгонять в дворники. Это было
из идеального мира.
В реальном есть много контор, где такое приветствуется — правда они все
попильные.
Posted via RSDN NNTP Server 2.1 beta
Re[9]: Проблема с перегрузкой бинарного оператора + в классе
От: GreyMan  
Дата: 09.11.13 12:53
Оценка:
Здравствуйте, RussianFellow, Вы писали:


RF>Я посидел в отладчике, проверил все варианты--всё нормально у меня работает. Надо только знать, где следует подчищать память из pGlobal и nGlobal.


Как насчет многопоточности?
Ну и rvalue references из С++11 не спасут отца русской демократии?
Re[7]: Проблема с перегрузкой бинарного оператора + в классе
От: Кодт Россия  
Дата: 11.11.13 07:49
Оценка:
Здравствуйте, RussianFellow, Вы писали:

RF>Я решил проблему -- создал глобальные переменные pGlobal типа long double* и nGlobal типа int.


Не решил, а перепрятал.

RF>В функции operator+ я выделяю память для pGlobal с помощью new [] (количество элементов этого массива совпадает с размерностью вектора), nGlobal я присваиваю значение размерности вектора и с помощью функции memmove я помещаю в pGlobal элемента поля massif моего класса myNVector.

RF>А в функции operator= я с помощью той же функции memmove я считываю данные из pGlobal в поле massif моего объекта, а полю n моего объекта я присваиваю значение nGlobal.

Если уж изобретать велосипед, то делать это хотя бы канонически, а не тревожными воспоминаниями о лихих 80-х и K&R C.
class MyVec
{
public:
  typedef long double element_type;
private: // потому что нечего кому ни попадя лазить в недра
  element_type* data_;
  size_t dim_;

public:
  // доступ

  size_t dim() const { return dim_; }
  element_type* data() const { return data_; } // читать-писать, но не создавать-удалять!

  element_type      & operator[](size_t i)       { assert(i<dim_); return data_[i]; }
  element_type const& operator[](size_t i) const { assert(i<dim_); return data_[i]; }

  // конструкторы-монструкторы

  explicit // чтобы не было соблазна приводить скаляр к вектору
  MyVec(size_t d = 0) : dim_(d), data_(d ? new element_type[d] : nullptr) {}
  ~MyVec() { delete[] data_; }

  MyVec(const MyVec& v) : dim_(v.dim_), data_(new element_type[v.dim_]) { std::copy(data_, data_+dim_, v.data_); } // вместо memmove
  MyVec(MyVec&& v) : dim_(v.dim_), data_(v.data_) { v.dim_ = 0; v.data_ = nullptr; } // C++11: перемещающий конструктор

  MyVec& operator=(const MyVec& v) { if(&v != this) MyVec(v).swap(*this); return *this; }
  MyVec& operator=(MyVec&& v)      {                      v .swap(*this); return *this; } // C++11: перемещающее присваивание

  void swap(MyVec& v) { std::swap(dim_, v.dim_); std::swap(data_, v.data_); }

  // операторы-моператоры

  MyVec operator + (MyVec const& v) const
  {
    assert(dim() == v.dim());
    MyVec result(dim()); // всё, на этом управление памятью закончили
    std::transform(data(), data()+dim(), v.data(), result.data(), std::plus<long double>());
    return result;
  }
};
Перекуём баги на фичи!
Re[9]: Проблема с перегрузкой бинарного оператора + в классе
От: saf_e  
Дата: 11.11.13 08:38
Оценка:
Здравствуйте, RussianFellow, Вы писали:

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


V>>Бррр. Может ты лучше на С#, Java программировать будешь. Потом же

V>>кому-то твое будет падать и он будет долго разгребаться — пожалей коллег.

RF>Я посидел в отладчике, проверил все варианты--всё нормально у меня работает. Надо только знать, где следует подчищать память из pGlobal и nGlobal.


Маленький совет, или учитесь как делать правильно (вам уже "over 9000" вариантов предложили). Или идите из С++ туда где ваши методы пригодяться, например сайты на РНР писать... Они все равно одноразовые, потом проще переделать.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.