Подтвердить что ты не робот

Почему программа не сбой при вызове функции-члена через нулевой указатель в С++?

#include "iostream"
using namespace std;
class A
{
public:
    void mprint()
    {
        cout<<"\n TESTING NULL POINTER";
    }
};

int main()
{
    A *a = NULL;
    a->mprint();
    return 0;
}

Я получаю вывод как "TESTTING NULL POINTER". Может кто-нибудь объяснить, почему эта программа печатает выходные данные вместо сбоев. Я проверил его на Dev С++, и компилятор aCC дал тот же результат.

4b9b3361

Ответ 1

Вы не используете какие-либо переменные-члены из A - функция полностью не зависит от экземпляра A, и поэтому сгенерированный код не содержит ничего, что разыгрывает 0. Это все еще undefined поведение - возможно, это может случиться с некоторыми компиляторами. Undefined поведение означает, что "все может случиться" - в том числе, что программа работает, как ожидал программист.

Если вы, например, make mprint virtual вы можете получить сбой - или вы не сможете его получить, если компилятор видит, что на самом деле ему не нужна виртуальная таблица.

Если вы добавите переменную-член в и распечатаете это, вы получите сбой.

Ответ 2

В соответствии с спецификацией С++, эта программа имеет поведение undefined, потому что вы вызываете функцию-член на пустом приемнике.

Причина, по которой это работает, заключается в том, что не виртуальные функции-члены обычно реализуются как обычные функции, которые принимают указатель "this" как неявный первый аргумент. Следовательно, если вы вызываете функцию-член по нулевому указателю, если вы не используете этот указатель, ваша программа не будет разбиваться. Конечно, вы не можете полагаться на это; действительный компилятор С++ может привести к сбою.

Однако виртуальные функции - это другая история, потому что функция, которая фактически вызвана, должна быть разрешена во время выполнения. Обычно это связано с интроспекцией таблицы виртуальных функций приемника. Таким образом, если вы попытаетесь вызвать виртуальную функцию-член на нулевом указателе, даже если функция te не имеет доступа к этому, это все равно приведет к сбою. Попробуйте это, если вам интересно!

Ответ 3

Результаты вызова функции-члена с использованием нулевого указателя на объект undefined behavour в С++, чтобы он мог что-то сделать.

В этом случае это, вероятно, потому, что оно переписало вашу функцию, поскольку это было похоже на это

void mprint(A* this);

и ваш вызов, подобный этому

mprint(0);

Таким образом, он просто назвал его, как если бы он был обычной функцией, и передал нулевой указатель в качестве параметра, который вы никогда в действительности не используете. Это объясняет, почему это не сбой, но компилятор волен делать что угодно.

Ответ 4

Простой ответ: поскольку mprint() не использует ни одну из переменных-членов класса

Подробный ответ. Когда вызывается метод класса, экземпляр класса передается на функцию вызываемого абонента (обычно как первый аргумент, однако, в некоторых соглашениях о вызовах, таких как __thiscall, это передается в регистр). Этот экземпляр класса используется для доступа ко всем переменным-членам, которые используются в методе calllee.

В этом случае этот экземпляр имеет NULL, но это не имеет никакого значения, поскольку в методе callle не используются переменные-члены. Попробуйте изменить код таким образом, чтобы вы печатали значение переменной-члена в методе mprint(), и вы получите сбой.

Ответ 5

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

#include <iostream>

class MagicInteger {

  public:

   static MagicInteger* fromInt (int x) {
     return reinterpret_cast<MagicInteger*>(x);
   }

   int getValue() {
     return static_cast<int>(reinterpret_cast<intptr_t>(this));
   }       

  private:
   // forbid messing around
   MagicInteger ();  
   MagicInteger (MagicInteger&);
   MagicInteger& operator=(const MagicInteger&);
};

int main (void) {
  MagicInteger* i = MagicInteger::fromInt(6);
  std::cout << "Value is " << i->getValue() << std::endl;
  return 0;
}

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

Эти две идиомы используются в Google Chrome javascript VM V8 для представления 31-битных целых чисел

Ответ 6

Это полный юридический вызов.

позволяет понять, как это работает

когда создается новый объект, создаются его переменные-члены.

Как насчет функций-членов? Функция-член не выделяется новостями, всегда есть одна копия всей функции-члена. По умолчанию переменная-член добавляется к каждой функции-члену, которая является этим указателем, который указывает на сам объект.
Когда нет объекта, который является указателем объекта, является нулевым значением. Это не имеет значения, потому что вы не обращаетесь к нему каким-либо образом. У вас возникнут проблемы, если вы используете этот указатель любой из переменной-члена в методе. Это связано с тем, что переменная-член недействительна в случае нулевого указателя.

в MFC у нас есть метод GetSafeHwnd() для CWnd. Это работает по тому же принципу.