Концепции C ++ Доступ к открытым методам - PullRequest
0 голосов
/ 17 ноября 2011
#include <iostream>

class Test {
public:
    int i;
    void print()
    {
        std::cout << "Hello" << std::endl;
    }
};

int main()
{
    class Test *p = NULL;
    p->print();
    (*p).print();
}

Output:

Hello
Hello

Я понимаю, что объекты-методы и переменные-члены хранятся в разных местах в памяти, но когда p назначается как NULL, как он может разрешить вызов Test::print()

Test6:~ 1001> g++ --version
g++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-46)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Test6:~ 1002> g++ manoj.cpp
Test6:~ 1003> ./a.out
Hello
Hello
Test6:~ 1004> cat manoj.cpp
#include <iostream>

class Test {
public:
int i;
void print()
{
std::cout << "Hello" << std::endl;
}
};

int main()
{
class Test *p = NULL;
p->print();
(*p).print();
}
Test6:~ 1005>

Ответы [ 3 ]

3 голосов
/ 17 ноября 2011

Если класс не имеет виртуальных функций (т. Е. Компилятор не создает vtable), все указатели на методы будут жестко закодированы в программе, таким образом, не требуя никакой переменной информации. Однако даже в этом случае у него не будет действительного указателя this, поэтому он все равно будет аварийно завершать работу.

0 голосов
/ 17 ноября 2011

То, что вы сделали, не является «хорошо разработанным фрагментом кода», но оно работает, потому что, даже если указатель this нулевой, вы никогда (ни явно, ни неявно) не разыменовываете его.

your *p (или p->) относится к классу Test, а Test::print не ссылается на что-либо внутри Test, поэтому ... поскольку нулевой тест всегда является тестом и вас не волнует его содержание, код компилируется и работает.

Но этот код может столкнуться с проблемами, если:

  • Test::print сделан виртуальным:в этом случае p->print() (или (*p).print()) должны пройти v-таблицу фактического объекта, чтобы определить, какую функцию вызывать.(*p может быть любым * Test -произведенным), но поскольку объекта нет, v-таблица не может быть обнаружена, и программа завершится сбоем.
  • Test сделан для того, чтобы иметь членаи ваша функция print создана для ее печати: в этом случае, поскольку p указывает на недопустимый блок памяти, ваша функция - при обращении к переменной-члену - попытается неявно разыменовать нулевой указатель, чтобы вычислить, где членявляется.И приведет к неверному адресу памяти, что приведет к сбою.

На самом деле вы только что вошли в эту серую область под названием " undefined поведенческая информация ": юристы языка (те,кто написал спецификации) ничего не сказал об этом (в частности: никогда не говорилось " ошибка (время выполнения) разыменовать неверный указатель ": они сказали " разыменование недопустимого указателя")приводит к неопределенному поведению , что едва означает «мы не хотим говорить вам, что должно произойти»).

Но так как автору компилятора нужно что-то делать (компилятор не может быть «неопределенным»),так как во время компиляции - при переводе строки кода - они не могут знать, каким будет значение указателя, они решили перевести код любым способом.

И так как выполнение кода не прерываетсялюбая граница процесса, она не падает.А поскольку доступ к «неинициализированному пространству в процессе» недоступен, не видны грязные значения и все выглядит хорошо.

Добро пожаловать в слово неуправляемых языков!

0 голосов
/ 17 ноября 2011

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

int main()
{
    // no need for 'class' keyword when declaring the pointer:
    Test* p = new Test(); // use default constructor provided by compiler
    p->print();
    (*p).print();
    // you also need to delete the pointer before returning, but that's irrelevant
    return 0; // you need this too
}
...