Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > перегрузка операций


Автор: serendip 27.1.2006, 02:22
Здравствуйте! smile
Кто-нибудь видит что-нибудь нехорошее в этих перегрузках?
Код

DMass::DMass(int m)
{  n=m;
   d=StrToInt(Form2->Edit3->Text);
   a=new float[n];
}
 DMass& DMass::operator=(const DMass &v)
 {
   for (int i=0; i<n; i++)
     a[i]=v.a[i];
      return *this;
 }

 DMass DMass::operator~()const
 { DMass rr(n);
           for (int i=0; i<n; i++)
           rr.a[i]= a[i]-d;
         return rr;
 }
....
DMass x(n);
   ...
     x=~x;

Работает(вторая) совсем непонятно, вадаёт один и тот же неожидаемый рез-т
(1,35244; 1,35244; 0; 0; ...0) И при повторном запуске ошибку(куда-то чего-то не по тому адресу)

Автор: JackYF 27.1.2006, 03:49
что такое DMass?
полный код в студию...

во второй функции не предусмотрено, мне кажется, разных размеров - откуда ты уверен, что они совпадают - опять же таки полный код DMass.

что такое переменная d и чему она равна (или может быть равна?)


Автор: threef 27.1.2006, 11:54
Цитата

Код

 DMass& DMass::operator=(const DMass &v)
 {
   for (int i=0; i<n; i++)
     a[i]=v.a[i];
      return *this;
 }





Я думаю здесь:
for (int i=0; i<n; i++)

i<n ?
либо i<v.n
либо
this->resize(v.getsize());

Автор: serendip 27.1.2006, 22:04
Итаак...
Код

class DMass
{    float* a;
        int n;
        float d;
       float step(float,float);
public:
        DMass(int = 1);
       ~DMass();
        void vvod1();
        void vvod2();
        void vivod1();
        void vivod2();
        DMass& operator=(const DMass&) ;
        DMass operator^(const DMass&) const; 
        DMass operator~()const;
};
//---------------------------------------------------------------------------
DMass::DMass(int m)
{  n=m;
   d=StrToFloat(Form2->Edit3->Text);
   a=new float[n];
}

Перегрузка та же. Есть ещё одна перегрузка, но она выдает точно такой же результатsmile
Код

DMass DMass::operator^(const DMass &v)const  
{ DMass f(n);
   for (int i=0; i<n; i++)
         f.a[i]=step(a[i],v.a[i]);

         return f;
}


Размер массивов всегда совпадает(так задумывалось)
Переменная d - просто обычное число, которое нужно вычесть из каждого эл-та массива.
Причём, без перегрузок"^"и"~" пр-мма работает нормально - всё вводит, выводит и даже присваивает объекты (т.е. срабатывает перегр. "=", я надеюсь)
Цитата

Я думаю здесь:
for (int i=0; i<n; i++)

i<n ?
либо i<v.n
либо
this->resize(v.getsize());

- не помогает.
Такая загадка... smile smile[code=cpp]

Автор: BreakPointMAN 28.1.2006, 00:00
Итак, для начала... smile Твой класс имеет члены-указатели:
Код

class DMass
{    float* a;
...

а в конструкторе ты выделяешь динамически память:
Код

DMass::DMass(int m)
{  n=m;
   d=StrToFloat(Form2->Edit3->Text);
   a=new float[n];
}

Это означает, что должны быть соответствующим образом определены деструктор данного класса, а также конструктор копирования и операция присваивания. В деструкторе ты должен высвобождать выделенную конструктором память (используя delete), а копировать и присваивать объекты данного класса нужно создавая в каждом из них "свои" динамические данные..

Оператор присваивания у тебя уже определен неверно:
Код

 DMass& DMass::operator=(const DMass &v)
 {
   for (int i=0; i<n; i++)
     a[i]=v.a[i];
      return *this;
 }

Автор: serendip 28.1.2006, 00:35
...ну что вам сказать...smile
ничего не понятно, кроме деструктораsmile
Могу только ответить:"А в учебнике так!":)
Т.е вы хотите сказать, что в "=" нужно создавать ещё один объект?

Автор: BreakPointMAN 28.1.2006, 00:44
Цитата(serendip @ 28.1.2006, 00:35 Найти цитируемый пост)

Могу только ответить:"А в учебнике так!":)

Либо в учебнике не так (например, нет выделения памяти с помощью new), либо учебник на свалку... smile
Цитата(serendip @ 28.1.2006, 00:35 Найти цитируемый пост)

Т.е вы хотите сказать, что в "=" нужно создавать ещё один объект?

Т.е. хочу сказать, что сначала нужно немного почитать про копирование и присваивание, конструкторы и деструкторы... smile

Автор: serendip 28.1.2006, 00:59
smile Не могу уже читать, так меня всё это досталоsmile
Архангельскийsmile

Автор: BreakPointMAN 28.1.2006, 01:31
Цитата(serendip @ 28.1.2006, 00:59 Найти цитируемый пост)

Архангельский

Архангельский для изучения среды C++ Builder хорош, но никак не для изучения языка C++... smile А понятия "конструктор", "деструктор" и пр. - это язык... Так что книгу по C++ в руки ... и читать! smile

Автор: serendip 28.1.2006, 10:15
smile Я в курсе, что такое конструктор и деструктор,
а вот
Цитата

конструктор копирования

и
Цитата

присваивать объекты данного класса нужно создавая в каждом из них "свои" динамические данные..

- не совсем понимаю..Думается мне, что в данном случае в этом нет необходимости.
Но мне очень необходмо понять, почему не работают перегрузки!smile
При объявлении объекта я каждый раз создаю в нём свои динамические данные, разве не так?

Автор: Fin 28.1.2006, 10:42
Скрытая логическая ошибка (в класофикации граблей № 2):
Код

DMass DMass::operator~()const
 { DMass rr(n);
           for (int i=0; i<n; i++)
           rr.a[i]= a[i]-d;
         return rr;
 }


В функции создается статический объект, при выходе из функции объект автоматически уничтожается. И ссылка на него некоретна.

Автор: Daevaorn 28.1.2006, 10:44
Цитата(Fin @ 28.1.2006, 10:42 Найти цитируемый пост)

В функции создается статический объект, при выходе из функции объект автоматически уничтожается. И ссылка на него некоретна.

Приглядись, там нет ссылки.

Автор: Fin 28.1.2006, 10:51
Это что return rr Да это не ссылка на объект. Но вывод за пределы функции результатов убитого уже экземпляра объекта.

Автор: Daevaorn 28.1.2006, 11:04
Fin
Тогда по твоей логике

Код

int f()
{
   return 123;
}


тоже не коректно?smile

Автор: Fin 28.1.2006, 11:12
Коректно. Компилятор скорее всего сделает такую последовательность команд аcсемблера.

Код

   mov ax,7B
   ret

Автор: Daevaorn 28.1.2006, 11:16
Fin
Хе. А в том случае вызовет конструктор копирования. А при большом везении просто соптимизирует.

Автор: Fin 28.1.2006, 11:16
Под статический объект в функции отводится память в стэке. При выходе из функции, весь стэк отведенный под функцию возврашается. И дальнейшая ссылка на данную область памяти некоректна.

Автор: Daevaorn 28.1.2006, 11:18
Fin
Никто не спорит. Просто ты забыл, что это С++, а не asm и компилятор занает что делает. Поверь
Добавлено @ 11:20
Цитата(Fin @ 28.1.2006, 11:16 Найти цитируемый пост)

Под статический объект в функции отводится память в стэке

А это совсем не верно. В стеке только локальные не статические переменные.

Автор: Fin 28.1.2006, 11:27
Цитата

В стеке только локальные не статические переменные.

Экземпляр объекта можно создать динамически и статически. Для функции статический экземпляр объекта локален. И тут я не вижу в чем не соответствие.

Цитата

Просто ты забыл, что это С++, а не asm и компилятор занает что делает. Поверь

Компилятору как то по барабану, что ты твориш, самое главное синтаксически правильно. Тебе искать глюки. Лучше сразу делать безглючно, чем весь день сидеть и вылавливать их.

Автор: Daevaorn 28.1.2006, 11:40
Цитата(Fin @ 28.1.2006, 11:27 Найти цитируемый пост)

Для функции статический экземпляр объекта локален

В С++ у панятия статический своё значение, не надо путать.

А глюков там нет

Код

int f()
{
   int a = 777;
   return a;
}


Это же у тебя вопросов не вызвает.

Автор: Void 28.1.2006, 11:40
Цитата(Fin @ 28.1.2006, 13:27 Найти цитируемый пост)
Экземпляр объекта можно создать динамически и статически. Для функции статический экземпляр объекта локален. И тут я не вижу в чем не соответствие.

Такое объяснение не соответсвует принятой в C++ терминологии, и вводит в заблуждение. Локальные объекты имеют automatic storage duration, а static storage duration — это совсем другое.

Цитата(Fin @ 28.1.2006, 13:27 Найти цитируемый пост)

Компилятору как то по барабану, что ты твориш, самое главное синтаксически правильно. Тебе искать глюки. Лучше сразу делать безглючно, чем весь день сидеть и вылавливать их.

Ты по-прежнему утверждаешь, что вот такой код:
Код
class A { ... };

A foo() {
    A obj;
    ...
    return obj;
}

Некорректен?

Автор: Fin 28.1.2006, 11:52
ОК, ребята делайте как хотите. Просто меня иногда достают глюки в неожиданных местах. Поэтому я сразу подстилаю соломку.

Автор: Void 28.1.2006, 12:03
Fin
Забавно, а как ты тогда возвращаешь объекты из функций? Исключительно в куче?

Автор: Fin 28.1.2006, 13:17
Ну чтож, идем к первоисточнику: т.е. к скомпилированному уже коду smile . Условия эксперемента, VC++6.0 Debag режим. Сама программа на С++.
Код

class A {
public:
    A(void) {};
    ~A(void) {};
};

A foo(void)
{
    A obj;
    return obj;
}

int main()
{
    foo();
    return 0;
}


Откомпилированный код, кусок, где идет вызов функции foo
Код

00401138   lea         eax,[ebp-4]
0040113B   push        eax
0040113C   call        @ILT+0(foo) (00401005)
00401141   add         esp,4
00401144   lea         ecx,[ebp-4]
00401147   call        @ILT+10(A::~A) (0040100f)

Первые две строки, в стеке под адрес компилятор зарезервировал место, и передает его через стэк в функцию foo. После вызова функции вызывает деструктор, передавая this объекта через регистр cx.
Сама функция foo
Код

7:    A foo(void)
8:    {                                                       
00401030   push        ebp
00401031   mov         ebp,esp
00401033   push        0FFh
00401035   push        offset __ehhandler$?foo@@YA?AVA@@XZ (00412e57)
0040103A   mov         eax,fs:[00000000]
00401040   push        eax
00401041   mov         dword ptr fs:[0],esp
00401048   sub         esp,48h
0040104B   push        ebx
0040104C   push        esi
0040104D   push        edi
0040104E   lea         edi,[ebp-54h]
00401051   mov         ecx,12h
00401056   mov         eax,0CCCCCCCCh
0040105B   rep stos    dword ptr [edi]
0040105D   mov         dword ptr [ebp-14h],0
9:        A obj;
00401064   lea         ecx,[ebp-10h]
00401067   call        @ILT+5(A::A) (0040100a)
10:       return obj;
0040106C   mov         eax,dword ptr [ebp+8]
0040106F   mov         cl,byte ptr [ebp-10h]
00401072   mov         byte ptr [eax],cl
00401074   mov         edx,dword ptr [ebp-14h]
00401077   or          edx,1
0040107A   mov         dword ptr [ebp-14h],edx
0040107D   lea         ecx,[ebp-10h]
00401080   call        @ILT+10(A::~A) (0040100f)
00401085   mov         eax,dword ptr [ebp+8]
11:   }
00401088   mov         ecx,dword ptr [ebp-0Ch]
0040108B   mov         dword ptr fs:[0],ecx
00401092   pop         edi
00401093   pop         esi
00401094   pop         ebx
00401095   add         esp,54h
00401098   cmp         ebp,esp
0040109A   call        __chkesp (00401550)
0040109F   mov         esp,ebp
004010A1   pop         ebp
004010A2   ret

Парочка коментариев:
Строка по адресу 00401067 идет вызов конструктора.
В адрес [ebp-10h] скидывается ссылка на экземпляр класса (this) Этот адрес находится в стэке самой функции foo.
Строка 0040106F идет копирование инфы с объекта созданного в функции foo в объект созданный в месте, где вызвана функция.
Строка 00401080 Идет вызов деструктора экземпляра, который принадлежит функции foo.
Строка 00401085 Идет возврат адреса экземпляра через регистр ax, который был передан ранее.

Теперь чуть усложним программу.
Код

#include <iostream.h>
class A {
public:
    A(void) {cout << "Constructor " << this << endl;};
    ~A(void) {cout << "Destructor " << this << endl;};
};

A foo(void)
{
    A obj;
    return obj;
}

int main()
{
    A s=foo();
    return 0;
}

Выдаваемый результат:
Цитата

Constructor 0x0012FF10
Destructor 0x0012FF10
Destructor 0x0012FF7C
Press any key to continue

Прошу заметить уважаемую публику, что конструктор для экземпляра с адресом 0x0012FF7C не был вызван. Компилятор пропустил на ура, без проблем.

Автор: BreakPointMAN 28.1.2006, 13:31
Цитата(serendip @ 28.1.2006, 10:15 Найти цитируемый пост)

- не совсем понимаю..Думается мне, что в данном случае в этом нет необходимости.

И зря тебе так думается. Даже если в конце-концов твой код заработает, неужели тебе не будет неприятно, что он написан криво?.. А если ты не сделаешь то, что тебе посоветовали, то будешь ловить, в лучшем случае, утечки памяти, либо, в худшем, Access Violation'ы. Тебе оно надо? Думаю, что нет...

Автор: Void 28.1.2006, 13:43
Цитата(Fin @ 28.1.2006, 15:17 Найти цитируемый пост)
Прошу заметить уважаемую публику, что конструктор для экземпляра с адресом 0x0012FF7C не был вызван.

12.8/15
И не ищи проблем на ровном месте.

Автор: BreakPointMAN 28.1.2006, 13:48
Цитата(Fin @ 28.1.2006, 13:17 Найти цитируемый пост)

Прошу заметить уважаемую публику, что конструктор для экземпляра с адресом 0x0012FF7C не был вызван. Компилятор пропустил на ура, без проблем.

Конструктор был вызван. Но не конструктор по умолчанию (определенный тобой), а конструктор копирования (созданный неявно).
Если ты задаешь его явно, то увидишь, что конструктор вызывается:
Код

 A(const A&){cout<<"Copy constructor " << this << endl;}

Автор: Fin 28.1.2006, 13:58
Еше чуть усложним класс
Код

#include <iostream.h>
class A {
public:
    A(void); 
    ~A(void); 
protected:
    int ch[25];
};

A::A(void)
{
    cout << "Constructor " << this << " ";
    //ch=new char[25];
    cout << "Adress of ch is " << ch << endl;
}

A::~A(void)
{
    cout << "Destructor  " << this << " ";
    cout << "Adress of ch is " << ch << endl;
    //if (ch !=NULL) delete [] ch;
    //ch=NULL;
}


A foo(void)
{
    A obj;
    return obj;
}

int main()
{
    A s=foo();
    return 0;
}

Получаем результаты:
Цитата

Constructor 0x0012FE50 Adress of ch is 0x0012FE50
Destructor  0x0012FE50 Adress of ch is 0x0012FE50
Destructor  0x0012FF1C Adress of ch is 0x0012FF1C
Press any key to continue


Теперь раскрываем ремы и делаем ch как ссылка.
Код

#include <iostream.h>
class A {
public:
    A(void); 
    ~A(void); 
protected:
    int *ch;
};

A::A(void)
{
    cout << "Constructor " << this << " ";
    ch=new int[25];
    cout << "Adress of ch is " << ch << endl;
}

A::~A(void)
{
    cout << "Destructor  " << this << " ";
    cout << "Adress of ch is " << ch << endl;
    if (ch !=NULL) delete [] ch;
    ch=NULL;
}


A foo(void)
{
    A obj;
    return obj;
}

int main()
{
    A s=foo();
    return 0;
}

Тут же получаем ошибку исполнения. предворительный результат до ошибки:
Цитата

Constructor 0x0012FF10 Adress of ch is 0x00320B00
Destructor  0x0012FF10 Adress of ch is 0x00320B00
Destructor  0x0012FF7C Adress of ch is 0x00320B00


Вывод: таким способом можно пользоваться, если нет в экземпляре объекта динамических выделений памяти.

Автор: Daevaorn 28.1.2006, 14:07
Цитата(Fin @ 28.1.2006, 13:58 Найти цитируемый пост)

Вывод: таким способом можно пользоваться, если нет в экземпляре объекта динамических выделений памяти.

Ну ты прям Америку открыл. Понятно что в таком случае нужно самому писать конструктор копирования и оператор присваивания. Ты отвлекся от первоночалного кода, а то что ты привел ничего не доказывает, кроме как пример не до конца дописанного класса.

Автор: BreakPointMAN 28.1.2006, 14:12
А если бы мы корректно определили конструктор копирования? smile
Как-нибудь так (пишу сгоряча, поэтому могу насажать грубых ляпов...):
Код

A::A(const A& rhs)
   {
    cout<<"Copy constructor "<<this<<endl;
    ch=new int[25];
    for(int i=0; i<25; i++)
       ch[i]=rhs.ch[i];
    cout << "Adress of ch is " << ch << endl;
   }


Так что совет тот же, как и serendip: читай про копирование и присваивание! smile
Добавлено @ 14:15
Кстати, в исходной программе, открывающей данный топик одна из главных проблем как раз в отсутствии копирующего конструктора и заключается... smile

Автор: LPBOY 28.1.2006, 15:49
Цитата(Void @ 28.1.2006, 11:40 Найти цитируемый пост)

Такое объяснение не соответсвует принятой в C++ терминологии, и вводит в заблуждение.
Локальные объекты имеют automatic storage duration, а static storage duration — это совсем другое.

ИМХО немного не так. Локальные объекты это те, которые объявлены в local scope.
Соответственно они могут иметь как automatic storage duration, так и static storage duration.

Автор: Void 28.1.2006, 18:12
LPBOY
+1 Сгоряча не оговорил все возможные варианты smile

Автор: serendip 28.1.2006, 20:20
Ужас какой...smile Пойду читать!smile Спасибо вам большое, честно говоря о конструкторе копирования я услышала от вас впервые..
И действительно, пр-мма вышла за область выделенной памяти и начала уже буквы выводить smile

Забавно..В 1-ом сообщении от BreakPointMAN написано самое главноеsmile
Ну с трудом как-то верилось..smile
Просто я не видела между динамически выделенной памятью и конструктором копирования никакой связи.
Прошу прощенья..smile

Автор: blackofe 2.2.2006, 21:09
Цитата(Fin @ 28.1.2006, 13:17)
Теперь чуть усложним программу.
Код

#include <iostream.h>
class A {
public:
    A(void) {cout << "Constructor " << this << endl;};
    ~A(void) {cout << "Destructor " << this << endl;};
};

A foo(void)
{
    A obj;
    return obj;
}

int main()
{
    A s=foo();
    return 0;
}

Выдаваемый результат:
Цитата

Constructor 0x0012FF10
Destructor 0x0012FF10
Destructor 0x0012FF7C
Press any key to continue

Прошу заметить уважаемую публику, что конструктор для экземпляра с адресом 0x0012FF7C не был вызван. Компилятор пропустил на ура, без проблем.

доусложним усложненную программу smile:

Код

class A {
public:
    A(void) {cout << "Constructor " << this << endl;};
    A(const A &a)                                {    cout << "copy A()" << this << endl; }
    ~A(void) {cout << "Destructor " << this << endl;};
};

A foo(void)
{
    A obj;
    return obj;
}

int main()
{
    A s=foo();
    return 0;
}


результат:

Код

Constructor 0012FDDB
copy A()0012FED7
Destructor 0012FDDB
Destructor 0012FED7


- была вызвана функция foo, внутри которой был создан объект типа A (первая выведенная запись).

- данный объект был возвращен из функции foo оператором return. при этом используется конструктор копирования, который мы сейчас доопределили явно (у тебя он не был определен, и за тебя его создал компилер) - вторая строчка вывода. обрати внимание - был создан новый объект - совершенно самостоятельный.

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

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

так что все правильно. и никакого криминала в возвращении объекта функцией нет.

Автор: Fin 3.2.2006, 01:39
blackofe Я с этим разобрался. В тот же день я проделал примерно тот же самый код, что и ты привел. Что мы имеем. Двойное создание и копирование класса. При наследовании класса, все время нужно поддерживать конструктор копирования.
И это все благодоря элементарной человеческой лени при написании кода. Я ленив, но не до такой степени.

Автор: Void 3.2.2006, 07:56
Fin
Знаешь, это возражения примерно из той же оперы, что и «никогда не пользуйтесь указателями, а то в вашем проекте заведутся маленькие злобные существа — меморилики».
Возвращать класс по значению или на куче — исключительно вопрос семантики данного класса.
Цитата(Fin @ 3.2.2006, 03:39 Найти цитируемый пост)
И это все благодоря элементарной человеческой лени при написании кода.

Элементарная человеческая лень привела к появлению Java и C#, где о таких вещах думать не надо. И это хорошо.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)