Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.56/9: Рейтинг темы: голосов - 9, средняя оценка - 4.56
быдлокодер
 Аватар для kravam
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,705

Методы вызываются через указатель на объект класса; Как компилятор определяет, какой из методов надо вызвать?

10.03.2013, 08:39. Показов 1944. Ответов 12
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Ребята, если вникать, то вообще ничё непонятно. Вот смотрите: пусть у нас есть абстрактный класс abstr (abstr.h) и в нём определено, например 10 функций виртуальных функции. Эти функции реализованы в каком-то статическом классе stats (файлы stats.h и stats.cpp). И пусть мы вызываем например две из них в main.cpp, вот так:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//main.cpp
#include <windows.h>
#include <stdio.h>
#include "abstr.h"
#include "stats.h"
 
 
int main() {
 SetConsoleCP(1251);
 SetConsoleOutputCP(1251);
 
 abstr* p= new stats; 
 
 p->foo_0();  
 p->foo_1();  
 delete p; 
 
 getchar ();
 return 0;
}
Теперь мы всё это дело компилим, то есть main.cpp отдельно, stats.cpp отдельно. И получаем 2 файла main.o и stats.o. Можно и нужно посмотреть их содержание утилитой nm:

Bash
1
2
nm main.o 
nm stats.o
Так, а теперь кропаем экзешник:

Bash
1
g++ -o main.exe main.o stats.o
И запускаем. Пока всё предсказуемо. А теперь внимание, фокус-покус. Измените main.cpp вот так (например):

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//main.cpp
#include <windows.h>
#include <stdio.h>
#include "abstr.h"
#include "stats.h"
 
int main() {
 SetConsoleCP(1251);
 SetConsoleOutputCP(1251);
 
 abstr* p= new stats; 
 
 p->foo_4();  
 p->foo_5();  
 delete p; 
 
 getchar ();
 return 0;
}
И снова скомпильте- вы получите *.o файлы, АБСОЛЮТНО ИДЕНТИЧНЫЕ тем, что были раньше (смотрим утилитой nm). Но экзешник у вас получится совершенно другой- а именно с функциями foo_4 и foo_5 вместо foo_0 и foo_1

Два абсолютно одинаковых набора сырцов (хотя сырцами называют исходники, но я щас говорю про *.o файлы) и два абсолютно разных экзешника!

++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++

Вы мне можете возразить- функции, которые вызовутся, определяются непосредственно во время исполнения. А вот ни фига подобного. Во время исполнения может быть определён тип статического классса, к которому будет неявно приведён указатель p- (тут, собсно, ничё нового. Но у меня для простоты один тип- stats). А вот функции, которые вызовутся, становятся известны только на этапе линковки и не позже! Хотя бы даже из того это следует, что: мы линкуем, допустим, второй пример и у нас в экзешнике оказываются ИМЕНО две нужных функции из 10-ти, foo_4 и foo_5, остальных и в помине там нет.

Так как компилятор из двух совершенно одинаковых наборов символов определяет- что в этот раз надо вставить в тело экзешника код функции такой-то, а в другой раз код функции такой-то. Где он в файле main.o видит имена этих функций?

И ещё раз повторю: аргумент "функции, которые необходимо вызвать, находятся через указатель p" (я, кстати, смотрел это дело в отладчике OllyDbg), неправилен. Ибо через этот указатель они могут найтись только лишь во время исполнения программы. А как мы выяснили, они к этому времени уже находятся в экзешнике. Причём только нужные функции, и никаких лишних.

В общем, ступор тот ещё.

Спасибо, кто откликнется. Файлы прилагаю.

это исходники будут
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
76
//main.cpp
#include <windows.h>
#include <stdio.h>
#include "abstr.h"
#include "stats.h"
 
 
int main() {
 SetConsoleCP(1251);
 SetConsoleOutputCP(1251);
 
 abstr* p= new stats; 
 
 p->foo_0();  
 p->foo_1();  
 delete p; 
 
 getchar ();
 return 0;
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++=
 
//abstr.h
#ifndef _ABSTR_H
#define _ABSTR_H
class abstr {
 public:
  virtual void foo_0  ()= 0;
  virtual void foo_1  ()= 0;
  virtual void foo_2  ()= 0;
  virtual void foo_3  ()= 0;
  virtual void foo_4  ()= 0;
  virtual void foo_5  ()= 0;
  virtual void foo_6  ()= 0;
  virtual void foo_7  ()= 0;
  virtual void foo_8  ()= 0;
  virtual void foo_9  ()= 0;
};
#endif
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
//stats.h
#ifndef _STATS_H
#define _STATS_H
#include "abstr.h"
class stats: public abstr {
 public:
  void foo_0  ();
  void foo_1  ();
  void foo_2  ();
  void foo_3  ();
  void foo_4  ();
  void foo_5  ();
  void foo_6  ();
  void foo_7  ();
  void foo_8  ();
  void foo_9  ();
};
#endif
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
//stats.cpp
#include "stats.h"
#include <stdio.h>
 
void stats::foo_0 ()  {printf ("ноль__\n");};
void stats::foo_1 ()  {printf ("один__\n");};
void stats::foo_2 ()  {printf ("два___\n");};
void stats::foo_3 ()  {printf ("три___\n");};
void stats::foo_4 ()  {printf ("четыре\n");};
void stats::foo_5 ()  {printf ("пять__\n");};
void stats::foo_6 ()  {printf ("шесть_\n");};
void stats::foo_7 ()  {printf ("семь__\n");};
void stats::foo_8 ()  {printf ("восемь\n");};
void stats::foo_9 ()  {printf ("девять\n");};
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++

компилить:
Bash
1
2
3
4
5
6
7
8
9
10
11
rem кропаем stats.o
del stats.o
g++ -c stats.cpp
 
rem кропаем main.o
del main.o
g++ -c main.cpp
 
rem кропаем main.exe
del main.exe
g++ -o main.exe main.o stats.o
А тут на всякий случай АБСОЛЮТНО одинаковые файлы main.o в первом и во втором случаях

//m_01.txt
00000000 b .bss
00000000 d .data
00000000 r .eh_frame
00000000 r .eh_frame$_ZN5abstrC2Ev
00000000 r .eh_frame$_ZN5statsC1Ev
00000000 r .eh_frame$getchar
00000000 r .rdata$_ZTI5abstr
00000000 r .rdata$_ZTS5abstr
00000000 r .rdata$_ZTV5abstr
00000000 t .text
00000000 t .text$_ZN5abstrC2Ev
00000000 t .text$_ZN5statsC1Ev
00000000 t .text$getchar
U _SetConsoleCP@4
U _SetConsoleOutputCP@4
00000000 T __ZN5abstrC2Ev
00000000 T __ZN5statsC1Ev
00000000 R __ZTI5abstr
00000000 R __ZTS5abstr
00000000 R __ZTV5abstr
U __ZTV5stats
U __ZTVN10__cxxabiv117__class_type_infoE
U __ZdlPv
U __Znwj
U ___cxa_pure_virtual
U ___main
U __filbuf
U __imp___iob
00000000 T _getchar
00000000 T _main

+++++++++++++++++++++++++++++++++++++++=

//m_45.txt
00000000 b .bss
00000000 d .data
00000000 r .eh_frame
00000000 r .eh_frame$_ZN5abstrC2Ev
00000000 r .eh_frame$_ZN5statsC1Ev
00000000 r .eh_frame$getchar
00000000 r .rdata$_ZTI5abstr
00000000 r .rdata$_ZTS5abstr
00000000 r .rdata$_ZTV5abstr
00000000 t .text
00000000 t .text$_ZN5abstrC2Ev
00000000 t .text$_ZN5statsC1Ev
00000000 t .text$getchar
U _SetConsoleCP@4
U _SetConsoleOutputCP@4
00000000 T __ZN5abstrC2Ev
00000000 T __ZN5statsC1Ev
00000000 R __ZTI5abstr
00000000 R __ZTS5abstr
00000000 R __ZTV5abstr
U __ZTV5stats
U __ZTVN10__cxxabiv117__class_type_infoE
U __ZdlPv
U __Znwj
U ___cxa_pure_virtual
U ___main
U __filbuf
U __imp___iob
00000000 T _getchar
00000000 T _main

Реезультаты сравнения stats.o самого с собой не выкладываю- и так понятно, он же не менялся от компиляции к компиляции, потому что не менялся stats.cpp
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
10.03.2013, 08:39
Ответы с готовыми решениями:

Явно указать какой из перегруженных методов надо вызвать
Смотрите, есть 2 перегруженных метода (exe), делают одно и тоже, возвращают немного разные результаты, более конкретно класс при помощи...

Как связать сигнал одного класса со слотом другого класса, содержащего указатель на объект этого класса?
Подскажите, пожалуйста, как связать сигнал одного класса со слотом другого класса, содержащего указатель на объект этого класса. Буду очень...

Как с помощью методов java.lang.reflect вызвать прикладные методы локального интерфейса?
Подскажите, пожалуйста, как с помощью методов java.lang.reflect вызвать прикладные методы локального интерфейса одного бина через другой...

12
Неэпический
 Аватар для Croessmah
18146 / 10730 / 2066
Регистрация: 27.09.2012
Сообщений: 27,030
Записей в блоге: 1
10.03.2013, 08:55
Например, компилятор может сгенерировать код:
посмотреть такую то память->вызвать функцию по адресу.

Добавлено через 9 минут
И еще почитайте про заглушку __cxa_pure_virtual
0
быдлокодер
 Аватар для kravam
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,705
10.03.2013, 08:56  [ТС]
Да он так и делает. Только почему-то по этим адресам кладутся нужные функции. Как он во время ЛИНКОВКИ (иначе мы бы имели в экзешнике все функции вместо фактически задействованных) определяет, какая функция нужная, а какая нет?
0
 Аватар для Kastaneda
5232 / 3205 / 362
Регистрация: 12.12.2009
Сообщений: 8,143
Записей в блоге: 2
10.03.2013, 10:24
Цитата Сообщение от kravam Посмотреть сообщение
Вы мне можете возразить- функции, которые вызовутся, определяются непосредственно во время исполнения. А вот ни фига подобного. Во время исполнения может быть определён тип статического классса, к которому будет неявно приведён указатель p- (тут, собсно, ничё нового. Но у меня для простоты один тип- stats). А вот функции, которые вызовутся, становятся известны только на этапе линковки и не позже!
Все не так. Адрес виртуальной функции определяется на этапе исполнения. Данная возможность реалиуется при помощи т.н. VPTR - указатель на таблицу виртуальных функций, который содержится в каждом объекте, если класс этого объекта содержит виртуальную функцию. Указатели ни к чему приводить не надо, просто смотриться VPTR по указателю и вызывается нужная функция.
Пример грязного использования VPTR - будет работать в MSVS, т.к. у них документировано, что VPTR располагается по нулевому смещение в объекте класса.
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
#include <iostream>
 
class A {
public:
    virtual void foo()
    {
        std::cout << "A::foo()" << std::endl;
    }
};
 
class B : public A
{
public:
    virtual void foo()
    {
        std::cout << "B::foo()" << std::endl;
    }
};
 
int main ()
{
    A *ob = new B;
 
    ((void(*)())(***((void(***)())ob)))(); // вызываем B::foo() через VPTR
}
вобщем можешь погуглить в эту сторону, чтоб лучше понять.

Добавлено через 3 минуты
вот нагуглил.

Добавлено через 31 минуту
Поковырялся в ассемблере (в листинге для кода выше). Инициализация VPTR происходит так - сначала VPTR настраивается на таблицу для класса А (все адреса вычисляются на этапе компиляции), потом в конструкторе В (до начала выполнения самомого конструктора) VPTR переписывается на указатель на таблицу для класса В.
Увидел еще одну интересную вещь - везде пишут что сначала вызывается конструктор базового класса, потом для наследника. Конечно это все так, но интересно было увидеть как это реально сделано (как то раньше не обращал на это внимания). Для примера выше - сначала вызывается конструктор В, в нем есть некий код, который будет выполнен до начала выполнения самого тела конструктора и, кроме всего прочего, там есть явный вызов конструктора А, после выполнения конструктора А есть еще какой-то код и только после всего этого начинается выполнение тело конструктора В. Т.е. можно умудриться поставить breakpoint в конструкторе В еще до вызова конструктора А.
Я к тому, что
везде пишут что сначала вызывается конструктор базового класса, потом для наследника.
технически получается, что сначала вызывается конструкотр наследника, из него уже конструктор базового класса, а после этого начинается выполнение конструктора наследника.
Я конечно примерно так это все и представлял, но почему удивился
0
быдлокодер
 Аватар для kravam
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,705
10.03.2013, 15:52  [ТС]
Цитата Сообщение от Kastaneda Посмотреть сообщение
Все не так
Естессно. Только и вы ре разобрамшись. А я разобрался и тоже всё, что я узнал вступает в противоречие доселе прочитанному, а именно- компилятор в экзешник кладёт тела ВСЕХ функций. И даже заботливо кладёт данные-строки, с которыми эти функции работают. То есть реально нужно 2, а кладёт все 10- проверено. Вот эти функции. Ненужные отмечены красным цветом. Искусственно прыгал на них отладчике и проверял- что выполнится. А куда прыгал, та функция выполнялась. Такие дела.

Это конечно на раз выяснялось, но я уж не полез в отладчик теорию опровергать. А полез бы- не было бы этой темы.

Не такой уж он и умный, получается, компилятор-то...
Миниатюры
Методы вызываются через указатель на объект класса; Как компилятор определяет, какой из методов надо вызвать?  
0
~ Эврика! ~
 Аватар для OhMyGodSoLong
1258 / 1007 / 74
Регистрация: 24.07.2012
Сообщений: 2,002
10.03.2013, 15:56
Мне вам рассказать про ключи и уровень оптимизации gcc по умолчанию или сами найдёте?
0
10.03.2013, 15:57

Не по теме:

Цитата Сообщение от kravam Посмотреть сообщение
эти функции реализованы в каком-то статическом классе
что такое статический класс в терминах с++?

0
Неэпический
 Аватар для Croessmah
18146 / 10730 / 2066
Регистрация: 27.09.2012
Сообщений: 27,030
Записей в блоге: 1
10.03.2013, 16:08
Цитата Сообщение от kravam Посмотреть сообщение
Не такой уж он и умный, получается, компилятор-то...
А может быть всё-таки не стоит вырезать функциональность и данные класса?
Как не крути класс - это целая сущность и не нужно урезать его функциональность.
0
быдлокодер
 Аватар для kravam
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,705
10.03.2013, 16:50  [ТС]
Цитата Сообщение от OhMyGodSoLong Посмотреть сообщение
Мне вам рассказать про ключи и уровень оптимизации gcc по умолчанию или сами найдёте?
Мы принцип разбираем, а не умничаем. Если компилятор в принципе не может не поместить реализацию всех функций, то он ВСЕГДА поместит реализацию всех 10-ти функций, хоть ты тресни. Сам-то пробовал оптимизировать или так, лишь бы засветиться? Я-то пробовал...

Добавлено через 1 минуту
Цитата Сообщение от Jupiter Посмотреть сообщение
что такое статический класс в терминах с++?
Вот так я и знал что кто-нибудь придерётся к терминам. Всё уже, решили вопрос.

Добавлено через 5 минут
Цитата Сообщение от Croessmah Посмотреть сообщение
А может быть всё-таки не стоит вырезать функциональность и данные класса?
Как не крути класс - это целая сущность и не нужно урезать его функциональность.
Да не то, чтобы не нужно- по-другому никак просто. Но на фига они в учебниках-то пишут ложь? Хотя может и не ложь. Читаешь же как обычно- есть класс, есть методы класса. Какие методы используются, те и будут в экзешнике и не методом больше (что правда). Так, а потом начинается тема про абстрактный класс, виртуальные функции и прочая. И дальше ситуация меняется- теперь в экзешнике будут присутствовать тела ВСЕХ функций! Только об этом в учебнике- молчок. Про таблицу виртуальных функций упомянут, а про то, что там будут тела всех функций- ни слова...
0
Неэпический
 Аватар для Croessmah
18146 / 10730 / 2066
Регистрация: 27.09.2012
Сообщений: 27,030
Записей в блоге: 1
10.03.2013, 16:53

Не по теме:

Цитата Сообщение от kravam Посмотреть сообщение
Но на фига они в учебниках-то пишут ложь?
Ну вот в школе учат, что на ноль делить нельзя, корень из отрицательных чисел находить... кто учился дальше школы, тот поймет о чем я :D



Цитата Сообщение от kravam Посмотреть сообщение
Только об этом- молчок в учебнике- молчок.
По-моему, везде пишут, что компилятор оптимизирует лишь то, что возможно.
0
быдлокодер
 Аватар для kravam
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,705
10.03.2013, 16:57  [ТС]
Угу. То есть я правильно вас понял, что вы всё это время знали в чём дело, что вот это вот заблуждение:
Хотя бы даже из того это следует, что: мы линкуем, допустим, второй пример и у нас в экзешнике оказываются ИМЕНО две нужных функции из 10-ти, foo_4 и foo_5, остальных и в помине там нет.
но стояли и снисходительно ждали, пока я сам дойду?
0
10.03.2013, 17:05

Не по теме:

Цитата Сообщение от kravam Посмотреть сообщение
но стояли и снисходительно ждали, пока я сам дойду?
Честно?
Написал пару простых прог, скомпилировал, дизасемблировал, потом отошел покушать и забыл про тему совершенно :rofl:

0
быдлокодер
 Аватар для kravam
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,705
10.03.2013, 17:08  [ТС]
ну хорошо что честно хоть
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
10.03.2013, 17:08
Помогаю со студенческими работами здесь

Как вызвать несколько методов у класса сразу?
Здравствуйте, нужна помощь. У меня есть класс: #pragma once using namespace System; using namespace System::Windows::Forms; ...

Как вызвать методы класса из его конструктора
Добрый день! Начал изучать язык, но столкнулся с проблемами(ответов в учебниках найти не смог): Имеется задача Нужно создать...

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

Как создать указатель на функцию-член класса и вызвать его?
Подскажите как правильно сделать, а в дальнейшем вызвать указатель на функцию член класса: #include &quot;stdafx.h&quot; #include...

Как вызывать методы класса по перебору массива с именами методов
Сабж... Вот код: class CApplication { public static function actionIndex() { echo &quot;Main!&quot;; } public...


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

Или воспользуйтесь поиском по форуму:
13
Ответ Создать тему
Новые блоги и статьи
Символьное дифференцирование
igorrr37 13.02.2026
/ * Программа принимает математическое выражение в виде строки и выдаёт его производную в виде строки и вычисляет значение производной при заданном х Логарифм записывается как: (x-2)log(x^2+2) -. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL3_image
8Observer8 10.02.2026
Содержание блога Библиотека SDL3_image содержит инструменты для расширенной работы с изображениями. Пошагово создадим проект для загрузки изображения формата PNG с альфа-каналом (с прозрачным. . .
Установка Qt-версии Lazarus IDE в Debian Trixie Xfce
volvo 10.02.2026
В общем, достали меня глюки IDE Лазаруса, собранной с использованием набора виджетов Gtk2 (конкретно: если набирать текст в редакторе и вызвать подсказку через Ctrl+Space, то после закрытия окошка. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru