Нельзя, но можно в Delphi, или аномалии Delphi и почему это работаетИсточник: RSDN Magazine #6-2004
Меня заинтересовал один момент в Delphi - почему, когда я забываю создать объект класса, программа вываливается, тем не менее, только внутри метода? Я решил протестировать эту особенность в различных вариациях:
Приведенный код компилируется и работает абсолютно без ошибок (по крайней мере, в Delphi 6). Почему? Сейчас будем разбираться. Обратите внимание на описание следующего класса:
И свойство, и метод его объявлены как private. Но, тем не менее, следующий код вполне замечательно компилируется:
Как видно, доступ к свойству и методу абсолютно свободен. Но это - широко известная аномалия защиты Delphi, заключающаяся в том, что код имеет доступ ко всем (даже приватным) данным и методам классов, описанных в том же модуле. Далее - более интересный момент. Во второй строчке вызывается метод несуществующего объекта! Это уже хитрость, связанная с особенностью компиляции классов. Дело в том, что при компиляции класса в код все имеющиеся у него функции сразу компилируются в функции с неявным аргументом - ссылкой Self. Так вот, этот Self и есть ссылка на память, отведенную под член-данные объекта. И эта память выделяется при вызове функций, помеченных как constructor, и высвобождается при вызове функций, помеченных как destructor! Получается, что все методы в объекте - это почти методы классов (Class methods, в терминологии Delphi), за исключением параметра Self! Т. е. разница между ними только в синтаксисе вызова и в неявном параметре. Деструкторы отличаются от них тем, что неявно содержат код освобождения памяти. Соответственно, конструкторы не содержат Self как аргумент, но возвращают его после выделения памяти под данные объекта. Теперь давайте сфокусируемся на методе класса:
Он содержит одну локальную переменную, память под которую опять же выделяется при компиляции класса! Теперь мы создадим объект класса стандартным конструктором и присвоим Self ссылку на этот объект. Опять всё работает! Delphi позволяет модифицировать переменную Self! Именно это и позволяет написать последнюю строчку в этой функции, ведь теперь-то память под данные у нас выделена! Ссылку на эту область памяти мы и вернём через параметр pSelf. Вернёмся опять к процедуре TForm1.Button1Click. Так как после вызова метода xObj.SomeFunc(x, xObj) переменная xObj содержит ссылку на выделенную область памяти, доступ к xObj.FProperty у нас уже есть - мы получили от операционной системы 4 байтика, требуемые для ее хранения. Поэтому y := xObj.FProperty работает тоже без проблем. Ну и, наконец, xObj.Free возвращает взятую нами память обратно операционной системе. После этого, несмотря на то, что там лежат вычисленные нами данные, доступа туда у нас уже нет (обращение к xObj.FProperty будет вызывать ошибку доступа к памяти Access Violation). Именно поэтому программа падает уже внутри методов, даже если память под объект не была выделена. Эту статью ни в коем случае нельзя рассматривать, как призыв пользоваться этими аномальными особенностями Delphi, просто автор надеется, что она поможет вам в дальнейшем выловить те ошибки, которые сама Delphi поймать никак не может. |