Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.71/7: Рейтинг темы: голосов - 7, средняя оценка - 4.71
инженер-системотехник
 Аватар для Haster
111 / 111 / 5
Регистрация: 10.03.2009
Сообщений: 533

О вызове функций классов

27.10.2011, 12:00. Показов 1512. Ответов 14
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте, товарищи!

У меня возник вопрос, почему работает данный код:
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
#include "stdafx.h"
#include <conio.h>
#include <iostream>
 
using namespace std;
 
class A
{
public:
    int a;
    int b;
    A()
    {
        cout<<"Constr\r\n";
    }
    void my(int a)
    {
        cout<<"my\r\n";
    }
    ~A()
    {
        cout<<"Destr\r\n";
    }
};
 
int _tmain(int argc, _TCHAR* argv[])
{
    A *a = new A();
    a->a = 5;
    a->b = 10;
    cout<<"&a = "<<&a<<"\r\n";
    cout<<"a = "<<a<<"\r\n";
    delete a;
    cout<<"a->a = "<<a->a<<"\r\n";
    cout<<"a->b = "<<a->b<<"\r\n";
 
    a = reinterpret_cast<A*>(0x403450L);
    cout<<"&a = "<<&a<<"\r\n";
    cout<<"a = "<<a<<"\r\n";
    a->my(0);
    cout<<"a->a = "<<a->a<<"\r\n";
    cout<<"a->b = "<<a->b<<"\r\n";
    _getch();
    return 0;
}
Вывод на консоль
C++
1
2
3
4
5
6
7
8
9
10
11
Constr
&a = 0012FF54
a = 0034C3B8
Destr
a->a = -17891602
a->b = -17891602
&a = 0012FF54
a = 00403450
my
a->a = 0
a->b = 0
т.е. мне не понятно, почему после удаления объекта, и изменения значения указателя a
сохраняется возможность вызвать функцию my.
Вероятно, функции в объектах вызываются каким-то странным образом?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
27.10.2011, 12:00
Ответы с готовыми решениями:

Множественное наследование, Перегрузка функций, Перегрузка операторов, Использование дружественных функций и классов, Использование шаблонов классов
Здравствуйте!!! Я бы хотел попросить помоч решить...ну или скинуть примеры таких задач, если вдруг у вас они завалялись на компе или...

Нюансы работы стека при вызове функций
Добрый день! # include &lt;iostream&gt; int foo(int b,int w){ int y=b+3; return y; } int main(){ int c=2;

Несовпадение типов данных при вызове функций
Код не компилируется из-за несовпадения типов переменных. Я уже запутался и не понимаю что не так) #include &lt;iostream&gt; #include...

14
 Аватар для kazak
3601 / 2742 / 355
Регистрация: 11.03.2009
Сообщений: 6,299
27.10.2011, 12:43
delete не удаляет объекты (переменные), а просто помечает занимаемую ими память как "свободно".
1
 Аватар для Revol'veR
23 / 23 / 4
Регистрация: 05.11.2010
Сообщений: 134
27.10.2011, 12:45
http://ru.wikipedia.org/wiki/Delete_%28C%2B%2B%29
1
 Аватар для kazak
3601 / 2742 / 355
Регистрация: 11.03.2009
Сообщений: 6,299
27.10.2011, 12:59
Цитата Сообщение от Haster Посмотреть сообщение
Вероятно, функции в объектах вызываются каким-то странным образом?
Код функций классов существует в единственном экземпляре, в этом плане они не отличаеются от обычных функций. Для их вызова в некоторых случаях не требуется даже создавать конкретный объект класса, достаточно сделать такой вызов <имя_класса>::<имя_функции>.
1
инженер-системотехник
 Аватар для Haster
111 / 111 / 5
Регистрация: 10.03.2009
Сообщений: 533
27.10.2011, 14:43  [ТС]
Я понимаю, что delete не удаляет объект,
мне интересно почему после изменения значения указателя (т.е. a указывает на другой адрес в памяти) все равно запись a->my(0) работает нормально.

Т.е. получается, что компилятор не обращает внимание на адреса, а тупо где видит конструкцию a->my(0) замещает ее на вызов нужной функции?
0
 Аватар для kazak
3601 / 2742 / 355
Регистрация: 11.03.2009
Сообщений: 6,299
27.10.2011, 15:23
Цитата Сообщение от Haster Посмотреть сообщение
Т.е. получается, что компилятор не обращает внимание на адреса, а тупо где видит конструкцию a->my(0) замещает ее на вызов нужной функции?
Типа того. Адрес в данном случае должен ссылаться на участок памяти, где располагаются данные экземпляра класса. На вызов функций значение адреса никак не влияет. Единственное, если функция работает с данными экземпляра класса, то неверное значение адреса, в большинстве случаев, приведет к краху программы.
1
 Аватар для hoot
101 / 22 / 7
Регистрация: 10.11.2010
Сообщений: 208
27.10.2011, 15:33
и после delete, есле не ошибаюсь, желательно присвоить значение ноль. Так что б не появлялось мусора в памяти.
1
инженер-системотехник
 Аватар для Haster
111 / 111 / 5
Регистрация: 10.03.2009
Сообщений: 533
27.10.2011, 15:47  [ТС]
Ага, спасибо за пояснения! Посмотрел ассемблерный листинг - действительно компилятор подставляет адрес функции в место вызова (точнее адрес элемента таблицы, элемент которой содержит инструкцию jmp на начало функции).
0
Эксперт С++
 Аватар для Mr.X
3225 / 1752 / 436
Регистрация: 03.05.2010
Сообщений: 3,867
27.10.2011, 16:25
Цитата Сообщение от Haster Посмотреть сообщение
Т.е. получается, что компилятор не обращает внимание на адреса, а тупо где видит конструкцию a->my(0) замещает ее на вызов нужной функции?
Ну почему же, очень даже обращает, ведь при вызове нестатической функции-члена в нее неявно передается адрес объекта под именем this.
В данном случае после присвоения указателю a нового значения при разыменовании этого указателя участок памяти, на который он указывает, интерпретируется как объект класса A, и, собственно, для этого объекта и вызывается функция my. Если тот мусор, который хранится на месте этого "объекта", способен выполнить его роль, то все пройдет нормально. Если же вы, например, объявите функцию my виртуальной, то все пройдет не так гладко.
2
инженер-системотехник
 Аватар для Haster
111 / 111 / 5
Регистрация: 10.03.2009
Сообщений: 533
27.10.2011, 17:28  [ТС]
Mr.X, я имеел ввиду, что компилятор не использует адрес объекта для вычисления местоположения функции в памяти (это наглядно демонстрируется возможностью вызова функции после установки указателя на другой адрес.

В дизассемблере это выглядит примерно так:

Assembler
1
2
3
4
5
6
...
1000: ...
1001: ...
1002: jmp 3000 ; прыжок на метод класса
...
2000: call 1002; вызов метода
Для виртуальных функций все сложнее, ибо присутствует виртуальная таблица, которая должна адресоваться через адрес объекта. Разве не так?
0
Заблокирован
27.10.2011, 19:10
Цитата Сообщение от Haster Посмотреть сообщение
Здравствуйте, товарищи!

У меня возник вопрос, почему работает данный код:
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
#include "stdafx.h"
#include <conio.h>
#include <iostream>
 
using namespace std;
 
class A
{
public:
    int a;
    int b;
    A()
    {
        cout<<"Constr\r\n";
    }
    void my(int a)
    {
        cout<<"my\r\n";
    }
    ~A()
    {
        cout<<"Destr\r\n";
    }
};
 
int _tmain(int argc, _TCHAR* argv[])
{
    A *a = new A();
    a->a = 5;
    a->b = 10;
    cout<<"&a = "<<&a<<"\r\n";
    cout<<"a = "<<a<<"\r\n";
    delete a;
    cout<<"a->a = "<<a->a<<"\r\n";
    cout<<"a->b = "<<a->b<<"\r\n";
 
    a = reinterpret_cast<A*>(0x403450L);
    cout<<"&a = "<<&a<<"\r\n";
    cout<<"a = "<<a<<"\r\n";
    a->my(0);
    cout<<"a->a = "<<a->a<<"\r\n";
    cout<<"a->b = "<<a->b<<"\r\n";
    _getch();
    return 0;
}
Вывод на консоль
C++
1
2
3
4
5
6
7
8
9
10
11
Constr
&a = 0012FF54
a = 0034C3B8
Destr
a->a = -17891602
a->b = -17891602
&a = 0012FF54
a = 00403450
my
a->a = 0
a->b = 0
т.е. мне не понятно, почему после удаления объекта, и изменения значения указателя a
сохраняется возможность вызвать функцию my.
Вероятно, функции в объектах вызываются каким-то странным образом?
Чтобы ответить на ваш вопрос, нужно разобраться, а что из себя представляет ваш объект? А ваш объект хранится в памяти, как обычная структура, состоящая из двух полей int a и int b. Больше ничего в вашем объекте нет, и никакие функции в объекте не хранятся.
Для всех объектов вашего класса имеется лишь одно определение ФУНКЦИИ void my(int a). Причем, так как эта функция определяется внутри тела класса, то она является встраиваемой, и код ее определения компилятор поместил во все места, где вы эту функцию вызываете. То есть это сделано компилятором еще до выполнения вашей программы. В чем разница вызова этой функции -члена класса, от обычной функции? Разница состоит лишь в том, что функции - члену класса передается неявно дополнительный первый параметр, который является указателем на объект вашего класса и носит название this.

Так что компилятору совершенно без разницы, удалили вы объект, или нет, он вставляет в код вызов этой функции и передает ей в качестве первого параметра значение вашего указателя a.
Как вы сами можете видеть из вывода вашей программы, сам указатель a никуда не делся! Его время жизни соотвентсвует телу функции main, в которой он, как локальная переменная, объявлен. Так что компилятору всегда известен адрес a. В вашем случае, судя по выводу программы, его адрес равен 0012FF54. Поэтому компилятор просто при вызове функции my в качестве первого неявного параметра, соответствующего this передает то значение, которое хранится в a. С этим параметром, то есть со значением, хранящемся в a, вы в функции my не работаете. Поэтому и никаких ошибок и не возникает!
2
Эксперт С++
 Аватар для Mr.X
3225 / 1752 / 436
Регистрация: 03.05.2010
Сообщений: 3,867
27.10.2011, 19:45
Цитата Сообщение от Haster Посмотреть сообщение
Mr.X, я имеел ввиду, что компилятор не использует адрес объекта для вычисления местоположения функции в памяти
Да, это верно, и из этого следует такой, например, курьез, что если в функции-члене класса есть статическая переменная, то она будет одна для всех объектов этого класса в текущем пространстве имен, следовательно никак не связанные объекты этого класса могут обмениваться через нее информацией.
Пример:
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
/////////////////////////////////////////////////////////////////////////////////////////
#include <iostream>
/////////////////////////////////////////////////////////////////////////////////////////
struct A
{
    int&  get_set_n()
    {
        static int n = 0;
        return n;
    }
};
/////////////////////////////////////////////////////////////////////////////////////////
int main()
{
    A  a;
    std::cout << "a.get_set_n() = "
              << a.get_set_n()
              << std::endl;
 
    a.get_set_n() = 5;
    A  b;
 
    std::cout << "b.get_set_n() = "
              << b.get_set_n()
              << std::endl;
}
С другой стороны не очень понятна ваша логика в высказывании
Цитата Сообщение от Haster Посмотреть сообщение
компилятор не использует адрес объекта для вычисления местоположения функции в памяти (это наглядно демонстрируется возможностью вызова функции после установки указателя на другой адрес.
Там же и данные-члены выводятся для нового адреса. Если так рассуждать, то и для их вывода адрес объекта не используется?
1
инженер-системотехник
 Аватар для Haster
111 / 111 / 5
Регистрация: 10.03.2009
Сообщений: 533
28.10.2011, 09:52  [ТС]
Mr.X, логика в том, что если бы компилятор использовал адрес объекта при вызове функции, то при изменении указателя не удалось бы вызвать данную функцию.
Ну а выводы элементов остались после модификации примера (раньше там еще были куски кода)

Вообще, спасибо за объяснения!!! )

Добавлено через 13 часов 28 минут
Сыроежка, спасибо за объяснения. Только в данном случае компилятор почему-то не встроил функцию в место вызова (судя по сгенерированному ассемблерному листингу). Интересно, почему?
Assembler
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
_wmain  PROC                        ; COMDAT
 
; 27   : {
 
    push    ebp
    mov ebp, esp
...
; 28   :         A *a = new A();
 
...
 
; 40   :         a->my(0);
 
    push    0
    mov ecx, DWORD PTR _a$[ebp]
    call    ?my@A@@QAEXH@Z              ; A::my
 
; 41   :         cout<<"a->a = "<<a->a<<"\r\n";
 
...
 
; 44   :         return 0;
 
    xor eax, eax
 
; 45   : }
 
CONST   SEGMENT
??_C@_04LMHPCPNB@my?$AN?6?$AA@ DB 'my', 0dH, 0aH, 00H   ; `string'
; Function compile flags: /Odtp /RTCsu /ZI
CONST   ENDS
;   COMDAT ?my@A@@QAEXH@Z
_TEXT   SEGMENT
_this$ = -8                     ; size = 4
_a$ = 8                         ; size = 4
?my@A@@QAEXH@Z PROC                 ; A::my, COMDAT
; _this$ = ecx
 
; 17   :         {
 
    push    ebp
    mov ebp, esp
    sub esp, 204                ; 000000ccH
    push    ebx
    push    esi
    push    edi
    push    ecx
    lea edi, DWORD PTR [ebp-204]
    mov ecx, 51                 ; 00000033H
    mov eax, -858993460             ; ccccccccH
    rep stosd
    pop ecx
    mov DWORD PTR _this$[ebp], ecx
 
; 18   :                 cout<<"my\r\n";
 
...
Т.е. видно, что функция вызывается при помощи команды call
0
Заблокирован
28.10.2011, 21:29
Цитата Сообщение от Haster Посмотреть сообщение
Mr.X, логика в том, что если бы компилятор использовал адрес объекта при вызове функции, то при изменении указателя не удалось бы вызвать данную функцию.
Ну а выводы элементов остались после модификации примера (раньше там еще были куски кода)

Вообще, спасибо за объяснения!!! )

Добавлено через 13 часов 28 минут
Сыроежка, спасибо за объяснения. Только в данном случае компилятор почему-то не встроил функцию в место вызова (судя по сгенерированному ассемблерному листингу). Интересно, почему?
Assembler
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
_wmain  PROC                        ; COMDAT
 
; 27   : {
 
    push    ebp
    mov ebp, esp
...
; 28   :         A *a = new A();
 
...
 
; 40   :         a->my(0);
 
    push    0
    mov ecx, DWORD PTR _a$[ebp]
    call    ?my@A@@QAEXH@Z              ; A::my
 
; 41   :         cout<<"a->a = "<<a->a<<"\r\n";
 
...
 
; 44   :         return 0;
 
    xor eax, eax
 
; 45   : }
 
CONST   SEGMENT
??_C@_04LMHPCPNB@my?$AN?6?$AA@ DB 'my', 0dH, 0aH, 00H   ; `string'
; Function compile flags: /Odtp /RTCsu /ZI
CONST   ENDS
;   COMDAT ?my@A@@QAEXH@Z
_TEXT   SEGMENT
_this$ = -8                     ; size = 4
_a$ = 8                         ; size = 4
?my@A@@QAEXH@Z PROC                 ; A::my, COMDAT
; _this$ = ecx
 
; 17   :         {
 
    push    ebp
    mov ebp, esp
    sub esp, 204                ; 000000ccH
    push    ebx
    push    esi
    push    edi
    push    ecx
    lea edi, DWORD PTR [ebp-204]
    mov ecx, 51                 ; 00000033H
    mov eax, -858993460             ; ccccccccH
    rep stosd
    pop ecx
    mov DWORD PTR _this$[ebp], ecx
 
; 18   :                 cout<<"my\r\n";
 
...
Т.е. видно, что функция вызывается при помощи команды call
Откровенно говоря, пока не могу сказать. Это может быть связано с опциями компилятора, с режимом запуска (отладка или нет) и т.д.

Но я смотрю код и не могу понять, а та ли эта функция? То есть в теле функции в цикле 81 раз записывается какое-то слово в область памяти
0
инженер-системотехник
 Аватар для Haster
111 / 111 / 5
Регистрация: 10.03.2009
Сообщений: 533
29.10.2011, 16:52  [ТС]
Сыроежка, функция та, не сомневайтесь.
Просто компилятор от Майкрософт вставляет такую вещь перед каждой функцией.
Мне тоже не совсем понятно зачем (кстати, копирует он инструкцию int 3), но скорей всего для защиты стека или что-то подобного.
Может кто-нибудь в курсе, зачем?
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
29.10.2011, 16:52
Помогаю со студенческими работами здесь

Программа выдаёт ошибку при вызове конструкторов разных классов в списке.Используется шаблон
В чём суть проблемы: в односвязном списке есть функция загрузки из файла. Вот она: template &lt;typename T&gt; int...

Какие нюансы в вызове виртуальных функций из конструктора и из деструктора?
Данный вопрос взят с перечня вопросов на собеседование, смысл мне не совсем понятен, вот и решил его задать тут. Может кто понял суть...

Объясните, пожалуйста, как работает передача переменных при вызове функций?
#include &lt;iostream&gt; #include &lt;conio.h&gt; using namespace std; void z(int *a,int lenght){ int *p; for...

Что конкретно означает оператор . (точка) при вызове каких либо функций?
Например, функция s.size() вычисляет длину строки s. Так есть где-нибудь определение этой точки? В Википедии написано &quot;Обращение к...

Где ошибка в вызове функций
Здравствуйте! Учусь работать с html и javascript, получила задание. Для решения поставленных задач нашла несколько функций javascript...


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

Или воспользуйтесь поиском по форуму:
15
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru