С Новым годом! Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 5.00/3: Рейтинг темы: голосов - 3, средняя оценка - 5.00
0 / 0 / 0
Регистрация: 04.06.2022
Сообщений: 24

Почему компилируется и выполняется такой код?

04.06.2022, 13:59. Показов 636. Ответов 8
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
У объекта типа A, указатель которого приведён к типу B*, нет функции voidB, а он её почему-то вызывает.
Более того, сигнатуры voidA и voidB отличаются, но код всё равно компилируется и работает.
Почему?
Как вообще происходит вызов функций и передача ей аргументов, есть ли что-нибудь почитать про это?
Я так понял, просто считываются байты на участках памяти и не важно, есть ли в них вообще какой-то смысл.
Но почему это происходит?

Почему даже шаблонный класс сккомпилировался, хотя ему недостаёт методов?

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
#include <iostream>
#include <string>
 
class A {
    std::string s;
public:                                                             
    A(const std::string& str) : s(str) { std::cout << "A::A()" << std::endl; }
    ~A() { std::cout << "A::~A()" << std::endl; }
    void voidA() { std::cout << "A::voidA " << s << std::endl; }
};
 
class B {
    int i;
public:                                                             
    B(int ii) : i(ii) { std::cout << "B::B()" << std::endl; }
    ~B() { std::cout << "B::~B()" << std::endl; }
    void voidB() { std::cout << "B::voidB " << i << std::endl; }
};
 
class C {
    char c;
public:                                                             
    C(int cc) : c(cc) { std::cout << "C::C()" << std::endl; }
    ~C() { std::cout << "C::~C()" << std::endl; }
    void voidC() { std::cout << "C::voidC " << c << std::endl; }
};
 
template<typename T> class F {
    T* t;
public:                             
    F(T* t_) : t(t_) { std::cout << "F::F()" << std::endl; }
    ~F() { delete t; std::cout << "F::~F()" << std::endl; }
    void voidA() { t->voidA(); }
    void voidB() { t->voidB(); }
    void voidC() { t->voidC(); }
};
 
int main() {
    F<A> fa(new A("strA"));
    fa.voidA();
 
    F<B> fb((B*)((void*)new A("strB")));
    fb.voidB();                    
 
    A fca("string_fca");
    fca.voidA();
    ((B*)((void*)&fca))->voidB();
 
    return 0;
}
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
04.06.2022, 13:59
Ответы с готовыми решениями:

SQL Почему не выполняется такой запрос в Builder C++
Почему не выполняется такой запрос в Builder C++ ? (этот запрос ищет по имени строка где есть &quot;1&quot;+любая последовательность букв)...

Почему компилируется код?
#include &lt;stdio.h&gt; int main(void) { double d = 1.12; f(d); f(&quot;Hello&quot;); return 0; }

Почему не компилируется код
Доброго времени суток, я только начал постигать азы великого и могучего, по этому не смейтесь если вам вопрос покажется слишком простым......

8
19491 / 10097 / 2460
Регистрация: 30.01.2014
Сообщений: 17,805
04.06.2022, 14:14
Цитата Сообщение от cdcodecpp Посмотреть сообщение
У объекта типа A, указатель которого приведён к типу B*, нет функции voidB, а он её почему-то вызывает.
Более того, сигнатуры voidA и voidB отличаются, но код всё равно компилируется и работает.
Почему?
Весь этот код UB. Т.е. на самом деле он не обязан работать. Скажу еще проще: так делать нельзя, хоть компилятор вам и позволяет. В С++ не все, что позволяет компилятор, является корректным.

Работает он потому, что в нем нет существенного нарушения ABI. Как только оно появится, код работать не будет, или будет работать не так, как вы ожидаете. Это как раз к вопросу о том, что почитать.
Например:
https://itanium-cxx-abi.github.io/cxx-abi/abi.html
https://uclibc.org/docs/psABI-x86_64.pdf
1
 Аватар для Pphantom
2256 / 1515 / 708
Регистрация: 17.03.2022
Сообщений: 4,868
04.06.2022, 14:16
Цитата Сообщение от cdcodecpp Посмотреть сообщение
Я так понял, просто считываются байты на участках памяти и не важно, есть ли в них вообще какой-то смысл.
Но почему это происходит?
Так ведь "тяжкое наследие" C. Хотите иметь надежный контроль типов - пишите на чем-нибудь другом. А тут такие штуки, хотя и формально запрещены стандартом, никак не контролируются (правда, и результат формально непредсказуем).
1
19491 / 10097 / 2460
Регистрация: 30.01.2014
Сообщений: 17,805
04.06.2022, 14:18
Цитата Сообщение от cdcodecpp Посмотреть сообщение
Почему даже шаблонный класс сккомпилировался, хотя ему недостаёт методов?
А это уже совершенно другой вопрос. По-хорошему ему место в отдельной теме.
Читайте про инстанцирование шаблонов классов. Например здесь: https://en.cppreference.com/w/... s_template

Если коротко: то, что не используется - не инстанцируется. В вашем коде метод voidC() у шаблона не вызывается, значит не инстанцируется. Как только вы добавите его вызов в код, сразу же получите ошибку компиляции.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12930 / 6798 / 1820
Регистрация: 18.10.2014
Сообщений: 17,205
04.06.2022, 14:31
Цитата Сообщение от cdcodecpp Посмотреть сообщение
Почему компилируется и выполняется такой код?
В мире С++ не бывает таких вопросов вообще: "почему код компилируется" или "почему код рабоатет".

Ваш код "компилируется" или "работает" по чистой случайности. Компилятор не может отловить всех ошибок. За чем-то вам придется следить самостоятельно. К С++ компилируемость или работоспособность вашего кода не имеет никакого отношения.

Цитата Сообщение от cdcodecpp Посмотреть сообщение
Почему даже шаблонный класс сккомпилировался, хотя ему недостаёт методов?
Сам шаблонный класс и каждый метод шаблонного класса - это фактически отдельные, во многом независимые шаблоны. Пока вы не используете какие-то методы шаблонного класса - они не инстанциируются и чего им там недостает значения не имеет. (Это сильно упрощено, но идея такова.)
0
0 / 0 / 0
Регистрация: 04.06.2022
Сообщений: 24
04.06.2022, 14:55  [ТС]
Спасибо тем, кто ответил, стало понятнее.

Цитата Сообщение от Pphantom Посмотреть сообщение
Так ведь "тяжкое наследие" C. Хотите иметь надежный контроль типов - пишите на чем-нибудь другом. А тут такие штуки, хотя и формально запрещены стандартом, никак не контролируются (правда, и результат формально непредсказуем).
Так я правильно понял, что просто считываются байты и что-то выводится, даже если не имеет смысла?

Добавлено через 2 минуты
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Сам шаблонный класс и каждый метод шаблонного класса - это фактически отдельные, во многом независимые шаблоны. Пока вы не используете какие-то методы шаблонного класса - они не инстанциируются и чего им там недостает значения не имеет. (Это сильно упрощено, но идея такова.)
Так и подумал, надо будет пополнить свои знания.

Добавлено через 2 минуты
Цитата Сообщение от DrOffset Посмотреть сообщение
Работает он потому, что в нем нет существенного нарушения ABI. Как только оно появится, код работать не будет, или будет работать не так, как вы ожидаете. Это как раз к вопросу о том, что почитать.
Я так понял, в ABI написано так же про то, как и с какими аргументами вызываются функции.
Получается, в таких манипуляциях с указателями и вызовами функций вообще почти любые методы с любыми аргументами можно вызывать, главное, чтобы программа не грохнулась.
Просто считывание байтов и воздействие на них?
0
19491 / 10097 / 2460
Регистрация: 30.01.2014
Сообщений: 17,805
04.06.2022, 14:57
cdcodecpp, если вы знаете как устроена конкретная реализация, да, можно.
0
 Аватар для Pphantom
2256 / 1515 / 708
Регистрация: 17.03.2022
Сообщений: 4,868
04.06.2022, 15:02
Цитата Сообщение от cdcodecpp Посмотреть сообщение
Так я правильно понял, что просто считываются байты и что-то выводится, даже если не имеет смысла?
В конечном счете да. Выше уже довольно подробно описали ситуацию: компилятор не контролирует осмысленность подобных действий, а итоговый бинарный код просто выполняет что-то, лежащее по определенному адресу.

Добавлено через 3 минуты
Цитата Сообщение от cdcodecpp Посмотреть сообщение
Получается, в таких манипуляциях с указателями и вызовами функций вообще почти любые методы с любыми аргументами можно вызывать, главное, чтобы программа не грохнулась.
Просто считывание байтов и воздействие на них?
Можно, но не нужно. Во-первых, в случае оговоренного в стандарте неопределенного поведения никто не гарантирует, что на другом компиляторе (и даже другой версии того же компилятора) или просто на другой конкретной системе результат будет таким же (причем, что особенно неприятно, программа может и не упасть, но сделать что-то не то). Во-вторых, поддержка кода с такими изысками - безумно сложное занятие, так что если вы не хотите, чтобы вас потом проклинали, не надо так делать.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
12930 / 6798 / 1820
Регистрация: 18.10.2014
Сообщений: 17,205
04.06.2022, 15:08
Цитата Сообщение от cdcodecpp Посмотреть сообщение
Получается, в таких манипуляциях с указателями и вызовами функций вообще почти любые методы с любыми аргументами можно вызывать, главное, чтобы программа не грохнулась.
Просто считывание байтов и воздействие на них?
Нет, нельзя. На практике, в реальной жизни главным источником пресловутого "неопределённого поведения" в С/С++ является не поведение сгенерированного кода (с ABI или без него), а поведение компилятора в процессе его генерации. В частности: при выполнении оптимизаций на основе правил неопределенного поведения.

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

Эквивалентным определением неопределенного поведения является следующее: компилятор имеет право транслировать код в предположении, что неопределенное поведение никогда не происходит, то есть обстоятельства, приводящие к неопределенному поведению никогда не возникают. В вашем примере у класса A нет метода voidB, а это значит что вызова такого метода через указатель на A не может быть потому что не может быть никогда. Компилятор будет использовать такие соображения при трансляции кода.

В частности, ваша программа не содержит ветвлений и все пути выполнения ведут к неопределенному поведению. Это означает, что компилятор имеет право полагать, что эта программа никогда не будет запущена на выполнение. И, соответственно, странслировать ее в пустой код.
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
04.06.2022, 15:08
Помогаю со студенческими работами здесь

О специализации шаблона: почему код компилируется?
Добрый день. Вроде бы, по правилам специализации шаблонов, можно только уточнять поведение шаблонного класса, но нельзя изменять его...

Как работает данный код? И почему не компилируется?
Обьясните пожалуйста как работает данный код, и скажите почему он не компилируется? И есть ли способ считать числа еще быстрее? ...

Почему указанный код не компилируется в Visual Studio?
#include &quot;pch.h&quot; #include &lt;iostream&gt; using namespace std; int j, x; main() { cin &gt;&gt; x; if (x &gt; 1) { j = x - 5; ...

[template] почему код не компилируется без конструктора
добрый вечер. вопрос поместил прямо в коде. http://rextester.com/AESO94403 #include &lt;iostream&gt; #include &lt;string&gt; ...

Почему не компилируется код с закрытым сконструированным компаратором?
using System; using System.Collections.Generic; namespace Test { class Collection&lt;T&gt; { public Collection(IComparer&lt;T&gt;...


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

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Новые блоги и статьи
Модель микоризы: классовый агентный подход 3
anaschu 06.01.2026
aa0a7f55b50dd51c5ec569d2d10c54f6/ O1rJuneU_ls https:/ / vkvideo. ru/ video-115721503_456239114
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR
ФедосеевПавел 06.01.2026
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR ВВЕДЕНИЕ Введу сокращения: аналоговый ПИД — ПИД регулятор с управляющим выходом в виде числа в диапазоне от 0% до. . .
Модель микоризы: классовый агентный подход 2
anaschu 06.01.2026
репозиторий https:/ / github. com/ shumilovas/ fungi ветка по-частям. коммит Create переделка под биомассу. txt вход sc, но sm считается внутри мицелия. кстати, обьем тоже должен там считаться. . . .
Расчёт токов в цепи постоянного тока
igorrr37 05.01.2026
/ * Дана цепь постоянного тока с сопротивлениями и напряжениями. Надо найти токи в ветвях. Программа составляет систему уравнений по 1 и 2 законам Кирхгофа и решает её. Последовательность действий:. . .
Новый CodeBlocs. Версия 25.03
palva 04.01.2026
Оказывается, недавно вышла новая версия CodeBlocks за номером 25. 03. Когда-то давно я возился с только что вышедшей тогда версией 20. 03. С тех пор я давно снёс всё с компьютера и забыл. Теперь. . .
Модель микоризы: классовый агентный подход
anaschu 02.01.2026
Раньше это было два гриба и бактерия. Теперь три гриба, растение. И на уровне агентов добавится между грибами или бактериями взаимодействий. До того я пробовал подход через многомерные массивы,. . .
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост.
Programma_Boinc 28.12.2025
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост. Налог на собак: https:/ / **********/ gallery/ V06K53e Финансовый отчет в Excel: https:/ / **********/ gallery/ bKBkQFf Пост отсюда. . .
Кто-нибудь знает, где можно бесплатно получить настольный компьютер или ноутбук? США.
Programma_Boinc 26.12.2025
Нашел на реддите интересную статью под названием Anyone know where to get a free Desktop or Laptop? Ниже её машинный перевод. После долгих разбирательств я наконец-то вернула себе. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru