Форум программистов, компьютерный форум CyberForum.ru

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
Red Planet
49 / 10 / 2
Регистрация: 20.09.2009
Сообщений: 263
#1

"Повторная" инициализация объекта - C++

12.02.2012, 21:18. Просмотров 642. Ответов 7
Метки нет (Все метки)

C++
1
2
3
4
5
class A {
    int x, y;
    public A() { x = y = 100; }
    public A(int xx, int yy) { x = xx; y = yy;}
};
C++
1
2
A a1 = A();
a1 = A(6, 4);
Меня интересует, что произойдет со значениями по умолчанию, которыми в первый раз был проинициализирован объект после инициализации вторым конструктором. Они останутся висеть где-то в памяти?

 Комментарий модератора 
Вроде не первый день на форуме. Зачем в чужую тему влезли?
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
12.02.2012, 21:18     "Повторная" инициализация объекта
Посмотрите здесь:

C++ чем отличается вызов объекта через "." и через "->"
Стратегия "получение ресурса есть инициализация" C++
C++ Что делать с ошибкой: C2440: инициализация: невозможно преобразовать "int **" в "int *"
Создание объекта класса сразу после его описания (между "}" и ";") C++
Warning C4244: инициализация: преобразование "__int64" в "int", возможна потеря данных C++
C++ Агрегатная инициализация std::array в сочетании с "new"
C++ 0xC0000005: Нарушение прав доступа при записи "0xcccccccc". Инициализация строк
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
silent_1991
Эксперт С++
4956 / 3032 / 149
Регистрация: 11.11.2009
Сообщений: 7,027
Завершенные тесты: 1
12.02.2012, 21:41     "Повторная" инициализация объекта #2
Red Planet, нет. В эту же память запишется новый объект.
Red Planet
49 / 10 / 2
Регистрация: 20.09.2009
Сообщений: 263
19.02.2012, 14:12  [ТС]     "Повторная" инициализация объекта #3
Очень удивился, когда увидел, что деструктор вызывается после вызова второго конструктора, а не до. Ведь логичнее было бы разрушить старый объект перед созданием нового, а здесь получается, что сначала имеем один объект, потом реинициализиреум его поля, потом вызываем деструктор, потом работаем с новымыми значениеями полей. Почему в таком странном порядке идет работа? Ладно еще, если не используем динамическую память (хотя зачем нам деструктор без нее?), - тогда внешне все в порядке. А вот если попробовать вызвать a1.show() после второй инициализации, то сначала вместо значения, хранящегося в p получим какое-то произвольное число, а потом вылетит исключение.

Ниже вывод программы и код.

Вывод.

Created
10 10
Other values
Created
Deleted
100 100
Для продолжения нажмите любую клавишу . . .
Код без указателя.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include "stdafx.h"
#include <iostream>
 
using namespace std;
 
class A {
    int x, y;
    public: A() { x = y = -1; cout << "Created default" << endl; }
            A(int xx, int yy) { x = xx; y = yy; cout << "Created" << endl; }
            ~A() { cout << "Deleted" << endl; }
            void show() { std::cout << x << " " << y << endl; } 
 
};
 
 
int _tmain(int argc, _TCHAR* argv[]) {
    A a1 = A(10, 10);
    a1.show();
    cout << "Other values" << endl;
    a1 = A(100, 100);
    a1.show();
    system("pause");
    return 0;
}
Код с указателем.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include "stdafx.h"
#include <iostream>
 
using namespace std;
 
class A {
    int x, y, *p;
    public: A() { x = y = -1; p = new int(100); cout << "Created default" << endl; }
            A(int xx, int yy) { x = xx; y = yy; p = new int(100); cout << "Created" << endl; }
            ~A() { delete p; cout << "Deleted" << endl; }
            void show() { std::cout << x << " " << y << " " << *p << endl; } 
 
};
 
 
int _tmain(int argc, _TCHAR* argv[]) {
    A a1 = A(10, 10);
    a1.show();
    cout << "Other values" << endl;
    a1 = A(100, 100);
    a1.show();
    system("pause");
    return 0;
}
silent_1991
Эксперт С++
4956 / 3032 / 149
Регистрация: 11.11.2009
Сообщений: 7,027
Завершенные тесты: 1
19.02.2012, 15:19     "Повторная" инициализация объекта #4
Red Planet, потому что вторая "инициализация" - не инициализация. Есть динамический захват ресурсов в конструкторе, святое правило - написать конструктор копии и оператор присваивания.
Ну а первое удаление - это не удаление старого объекта, как вы думаете, а удаление временного объекта, созданного в строке a1 = A(100, 100);, которым неверно инициализируется старый. Именно из-за этого удаления и использования оператора присваивания по умолчанию происходил баг с повторным освобождением памяти.
DU
1480 / 1056 / 45
Регистрация: 05.12.2011
Сообщений: 2,279
19.02.2012, 15:30     "Повторная" инициализация объекта #5
Created - создался объект а1
10 10 // печать объекта а1
Other values
Created - создался временный объект типа A, который передается в оператор =
.. тут в промежутке из этого временного объекта копируются данные в объект a1
Deleted - тут этот временный объект умирает

100 100 // печать объекта а1
Для продолжения нажмите любую клавишу . . .

Можете в трейсы добавить печать значения указателя this, тогда будет видно какие объекты создаются и умирают.
silent_1991
Эксперт С++
4956 / 3032 / 149
Регистрация: 11.11.2009
Сообщений: 7,027
Завершенные тесты: 1
19.02.2012, 15:38     "Повторная" инициализация объекта #6
Пояснения:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
int main()
{
    // Здесь, по идее, происходит два действия: сначала конструктор с
    // параметрами конструирует временный объект, который затем с помощью
    // конструктора копии копируется в объект a1. Доказать это просто -
    // достаточно поместить конструктор копии в приватную область, и код не
    // скомпилируется. Однако все эти промежуточные действия соверщенно не
    // нужны, и компилятор выкидывает их, оставляя конструирование объекта a1
    // сразу с помощью конструктора с параметрами, т.е. код становится
    // эквивалентен A a1(10, 10);
    A a1 = A(10, 10); // Вывод "Created"
    
    a1.show(); // Вывод "10 10 100"
    cout << "Other values" << endl;
    
    // Следующую строку можно разделить на три:
        // Воспринимаем __tmp как временный объект.
        // A __tmp(100, 100); // Вывод "Created"
        
        // Копируем временный объект в a1
        // a1.operator=(__tmp);
        
        // Воспринимаем эту строку как полное удаление временного
        // объекта, а не простой вызов деструктора. Сути это, однако,
        // не меняет, нам было важно то, что для временного объекта
        // вызвался деструктор.
        // __tmp.~A(); // Вывод "Deleted"
    // Благодаря тому, что оператор присваивания не был реализован,
    // использовался предоставляемый по умолчанию оператор присваивания,
    // побайтно копирующий данные из одного объекта в другой. Т.е. был
    // скопирован сам указатель из __tmp, а не его значение. Затем, после
    // копирования __tmp удаляется и, соответственно, память, на которую теперь
    // указывает как __tmp.p, так и a1.p, становится невалидной. Ну а дальше
    // всё ясно, при обращении к невалидной памяти получаем дулю.
    a1 = A(100, 100); // Вывод "Created"; Вывод "Deleted"
    
    a1.show(); // Вывод "100 100 абырвалг"
    
    // Вывод " Deleted"
    return 0;
}
Red Planet
49 / 10 / 2
Регистрация: 20.09.2009
Сообщений: 263
25.02.2012, 21:57  [ТС]     "Повторная" инициализация объекта #7
Я не знал о временном объекте, который создается перед инициализацией и присваиванием. Действительно, написание операторов все логично объясняет. Операторы взяты со Страуструпа.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
A::A(const A &right) {
    p = new A;
    *p = *(right.p);
    x = right.x;
    y = right.y;
}
 
A& A::operator =(const A &right) {
    if (&right != this) {
        delete p;
        p = new int;
        *p = *(right.p);
        x = right.x;
        y = right.y;
    }
    return *this;
}
Надо будет только поразбираться с возвращаемыми значениями A& и A. Там у него не одна страница про это написано, но это завтра.

Следующее. Что скажете насчет такого кода? Все ли правильно?

Смысл такой. При сложении двух объектов сумма должна "знать" правого операнда. Поэтому заведен указатель *p, который проявляется только при сложении, в остальных случаях он никуда не указывает. Приведенный код работает, однако меня смущает то, что два указателя ссылаются на одно и тоже значение. См. оператор сложения, в нем строка

C++
1
p = right.p;
При применении

C++
1
2
A a4 = a3;
A a5 = a3;
еще два объекта, у которых есть указатель, ссылающихся на тот же объект - правый операнд того самого сложения.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <vcl>
#include <iostream>
#include <list>
#pragma hdrstop
 
using namespace std;
 
class A {
    int x, y; A *p;
 
    public:
        A(int xx, int yy, A *p);
 
        A(int xx = -1, int yy = -1) :
            x(xx), y(yy), p(0) {}
 
        A& operator =(const A&);
        A operator +(const A&);
        bool operator ==(const A&);
        void show();
 
        A get_p_value() const { return *p; }
};
 
void A::show() {
    if (p != 0)
        cout << x << " " << y << " Right was: " << p->x << " " << p->y << endl;
    else
        cout << x << " " << y << " Right was: unknown" << endl;
}
 
A::A(int xx, int yy, A *pp) {
    x = xx;
    y = yy;
    p = pp;
}
 
A& A::operator =(const A& right) {
    if (this != &right) {
        p = right.p;
        x = right.x;
        y = right.y;
    }
    return *this;
}
 
A A::operator +(const A &right) {
    return A(x + right.x, y + right.y, const_cast<A*>(&right));
}
 
bool A::operator ==(const A &right) {
    return x == right.x && y == right.y && p == right.p;
}
 
#pragma argsused // No warning if function args are not in use.
// But there is no warning while compiling project. Why?
int _tmain(int argc, _TCHAR* argv[]) {
    list<A> lst = list<A>();
    A a1 = A(10, 10), a2(5, 7), a3;
    lst.push_back(a1);
    lst.push_back(a2);
 
    cout << "List before processing" << endl;
    for (list<A>::iterator it = lst.begin(); it != lst.end(); it++)
        it->show();
 
    a3 = a1 + a2;
    lst.remove(a3.get_p_value());
 
    cout << endl << "List after processing" << endl;
    for (list<A>::iterator it = lst.begin(); it != lst.end(); it++)
        it->show();
 
    system("pause");
}
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
25.02.2012, 23:26     "Повторная" инициализация объекта
Еще ссылки по теме:

Для агрегатного объекта требуется инициализация с использованием "{.}" C++
C++ Инициализация массива: ошибка "array must be initialized with a brace-enclosed initializer"
C++ Инициализация string с подключенной "тайной Страуструпа"
Error C2440: инициализация: невозможно преобразовать "void *" в "listnode *". подскажите, что можно сделать? C++
Перегрузить операторы "=", "+=" так, чтобы производилось сложение строки и объекта C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
silent_1991
Эксперт С++
4956 / 3032 / 149
Регистрация: 11.11.2009
Сообщений: 7,027
Завершенные тесты: 1
25.02.2012, 23:26     "Повторная" инициализация объекта #8
Red Planet, ну так не присваивайте указатели в операторе присваивания))
Yandex
Объявления
25.02.2012, 23:26     "Повторная" инициализация объекта
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru