В Java все переменные, которые вы объявляете, на самом деле являются «ссылками» на объекты (или примитивы), а не самими объектами.
При попытке выполнить один метод объекта , ссылка просит живой объект выполнить этот метод. Но если ссылка ссылается на NULL (ничего, нуль, void, nada), то нет способа, которым метод будет выполнен. Тогда runtime сообщит вам об этом, выбросив исключение NullPointerException.
Ваша ссылка «указывает» на нуль, таким образом, «Null -> Pointer».
Объект живет в памяти виртуальной машины пространство и единственный способ доступа к нему - использовать ссылки this
. Возьмем этот пример:
public class Some {
private int id;
public int getId(){
return this.id;
}
public setId( int newId ) {
this.id = newId;
}
}
И в другом месте вашего кода:
Some reference = new Some(); // Point to a new object of type Some()
Some otherReference = null; // Initiallly this points to NULL
reference.setId( 1 ); // Execute setId method, now private var id is 1
System.out.println( reference.getId() ); // Prints 1 to the console
otherReference = reference // Now they both point to the only object.
reference = null; // "reference" now point to null.
// But "otherReference" still point to the "real" object so this print 1 too...
System.out.println( otherReference.getId() );
// Guess what will happen
System.out.println( reference.getId() ); // :S Throws NullPointerException because "reference" is pointing to NULL remember...
Это важно знать - когда больше нет ссылок на объект (в пример выше, когда reference
и otherReference
оба указывают на null), тогда объект «недоступен». Мы не можем работать с ним, поэтому этот объект готов к сбору мусора, и в какой-то момент VM освободит память, используемую этим объектом, и выделит другую.
Они могут закончиться, при попытке выполнить вызов виртуальной функции от конструктора или деструктора. Так как Вы не можете выполнить вызов виртуальной функции от конструктора, или деструктор (объект производного класса не был создан или был уже уничтожен), это называет версию базового класса, которая в случае чистой виртуальной функции, не существует.
(См. живую демонстрацию здесь )
class Base
{
public:
Base() { doIt(); } // DON'T DO THIS
virtual void doIt() = 0;
};
void Base::doIt()
{
std::cout<<"Is it fine to call pure virtual function from constructor?";
}
class Derived : public Base
{
void doIt() {}
};
int main(void)
{
Derived d; // This will cause "pure virtual function call" error
}
А также стандартный случай вызывания виртуальной функции от конструктора или деструктора объекта с чистыми виртуальными функциями, можно также получить чистый вызов виртуальной функции (на MSVC, по крайней мере), если Вы вызываете виртуальную функцию после объекта, был уничтожен. Очевидно, это - довольно плохая вещь попытаться сделать, но если Вы работаете с абстрактными классами как интерфейсы, и Вы портите тогда, это - что-то, что Вы могли бы видеть. Возможно более вероятно, если Вы используете считаемые интерфейсы, на которые ссылаются, и Вы имеете касательно ошибки количества или если у Вас есть объектное состояние состязания разрушения использования/объекта в многопоточной программе... Вещь об этих видах purecall состоит в том, что часто менее легко постигнуть то, что продолжается как проверка на 'обычных подозреваемых' в виртуальных вызовах в ctor, и dtor подойдет чистый.
Для помощи с отладкой этих видов проблем, в различных версиях MSVC, можно заменить purecall обработчик библиотеки времени выполнения. Вы делаете это путем предоставления собственной функции эту подпись:
int __cdecl _purecall(void)
и соединение его перед соединением библиотеки времени выполнения. Это дает ВАМ контроль того, что происходит, когда purecall обнаруживается. Как только Вы имеете контроль, можно сделать что-то более полезное, чем стандартный обработчик. У меня есть обработчик, который может обеспечить отслеживание стека того, где purecall произошел; посмотрите здесь: http://www.lenholgate.com/blog/2006/01/purecall.html для получения дополнительной информации.
(Отмечают, можно также назвать _set_purecall_handler () для установки обработчика в некоторых версиях MSVC).
Обычно, когда Вы вызываете виртуальную функцию через висячий указатель - скорее всего, экземпляр был уже уничтожен.
могут быть более "творческие" причины, также: возможно, Вам удалось отрезать часть Вашего объекта, где виртуальная функция была реализована. Но обычно это просто, что экземпляр был уже уничтожен.
Я предположил бы, что существует vtbl, созданный для абстрактного класса по некоторой внутренней причине (это могло бы быть необходимо для своего рода информации о типе выполнения), и что-то идет не так, как надо, и реальный объект получает его. Это - ошибка. Тот один должен сказать, что что-то, чего не может произойти.
Чистое предположение
редактирование: похож, я неправ в рассматриваемом случае. OTOH IIRC некоторые языки действительно позволяет вызовы vtbl из деструктора конструктора.
Вот подлый путь к нему для случая. Я имел, это по существу происходит со мной сегодня.
class A
{
A *pThis;
public:
A()
: pThis(this)
{
}
void callFoo()
{
pThis->foo(); // call through the pThis ptr which was initialized in the constructor
}
virtual void foo() = 0;
};
class B : public A
{
public:
virtual void foo()
{
}
};
B b();
b.callFoo();