Глава 8
Источником наиболее сложных для обнаружения ошибок являются паразитные
указатели. ПУ образуется в тех случаях, когда после оператора delete
освободившего участок памяти, не следует переприсвоение указателя.
При попытке использовать такой указатель, результат будет непредсказуем.
(ведь облась памяти может содержать что угодно) если очень повезет
то программа просто зависнет.
Будьте осторожны при использовании указателей для которых вызывался
оператор delete. Указатель по прежнему будет содержать адрес области
памяти, но по этому адресу уже могут находиться другие данные.
Поэтому во избежание неприятностей после освобождения указателя
присваивайте ему значение 0. Это обезопасит указатель.
int main()
{
long * pLong = new long;
std::cout << "*pInt: " << *pInt << std::endl;
Результат :
*pInt: 10
Анализ программы:
typedef unsigned short int USHORT;
int main()
{
USHORT * pInt = new USHORT;
Теперь выводим на экран это значение , а затем освобождаем это место в
динамической памяти и освобождаем сам указатель.
std::cout << "*pInt: " << *pInt << std::endl;
Вот тут то указатель и оказался зависшим-паразитным!
Объявляем новый указатель на место под тип переменной в динамической памяти
и инициализируем (заносим в динамическую память значение) его.
Затем выводим на экран это значение из динамической памяти через
указатель на это место.(на эту переменную)
long * pLong = new long;
В следующей строке по адресу pInt записывается значение 20,
но эта операция некорректна, так как выделенная для этого указателя
память была освобождена, и теперь там возможно находятся чужие
данные, и мы этой операцией уничтожим их!Что может привести к
катастрофическим последствиям!!
Далее это новое значение из динамич. памяти выводится на экран.
Как и ожидалось оно равно 20.
Далее выводится значение на которое указывает указатель pLong
К удивлению там совсем не то что мы ожидали увидеть!(объяснение стр 233)
Правильно эта программа должна быть написана так:
typedef unsigned short int USHORT;
int main()
{
long * pLong = new long;
pInt = new USHORT;//вот строка, которой не хватало!
// Теперь можно поместить туда новое значение!
*pInt = 20;
std::cout << "*pInt: " << *pInt << std::endl;
В чем разница между пустым и паразитным указателем?
при удалении указателя освобождается лишь память на которую он
указывает, а сам указатель продолжает существовать. Такой указатель
называется паразитным.
Если присвоить такому указателю нулевое значение, написав myPtr = 0; ,
то паразитный указатель станет пустым или нулевым указателем.
Обычно если удалить указатель дважды (первый раз как настоящий,
второй - как паразитный), то результат этих действий будет непредсказуем.
в лучшем случае программа зависнет. При удалении пустого (нулевого)
указателя такого не случится, он совершенно безопасен.
Использование паразитного или нулевого указателя (например указать
myPtr = 5;)недопустимо, поскольку это приведет к серьезной ошибке.
Применение пустого указателя вызовет ошибку во время компиляции,
а применение паразитного - ошибку во время выполнения программы.
В этом и состоит еще одно преимущество пустого указателя -
некорректные операции с ним выявляются компилятором на раннем этапе
и проще исправляются.
Вверх
Ключевое слово const при объявлении указателей можно использовать
перед указанием типа, после него а так же в обоих местах.
Все нижеприведенные объявления допустимы.
pOne;-указатель на константу типа int. Следовательно значение
на которое он указывает не может быть изменено.
pTwo;-является константой- указателем на тип int. Само целое число
может быть изменено, но адрес в указателе pTwo -нет.
pThree;-объявлен как константа -указатель на константу типа int.
Это значит что он всегда указывает на одну и ту же область памяти,
и значение находящееся по этому адресу тоже не может изменяться.
Вся тонкость заключается во взаиморасположении ключевого слова const
и того, что объявляется константой. Если справа от ключевого слова
const находится тип, то константой будет объявляемое значение.
Если справа от ключевого слова const находится имя, то константой будет
сам указатель.
В уроке 6 ООП использование ключевого слова const при объявлении
функций-членов уже рассматривалось. Если объявить метод класса как
const(постоянный), то он не сможет изменить значение ни одного из членов
класса, а при попытке сделать это, компилятор возвратит сообщение об ошибке.
Если указатель объявлен на объект, являющийся постоянным, то функции
которые можно вызвать с его помощью, так же должны быть постоянными.
Посмотрите программу:
// Listing 8.10
// Using pointers with const methods
#include <iostream>
using namespace std;
class Rectangle
{
void SetLength(int length) {
int GetWidth() const {
private:
Rectangle::Rectangle()
{
Rectangle::~Rectangle()
{
int main()
{
Rectangle* pRect = new Rectangle;
cout << "pRect width: " << pRect->GetWidth()
pRect->SetWidth(10);
cout << "pRect width: " << pRect->GetWidth()
Результат:
Анализ:
Строка 14 -объявление постоянного метода-члена GetWidth()
Строка 33 -указатель pConstRect на постоянный объект этого же класса
36-41 значения переменных класса выводятся на экран
43 -указатель pRect используется для присвоения ширине прямоугольника
значения 10.
44 -была попытка использовать указатель pConstRect,
но он был объявлен как указатель на константу,
в связи с чем наличие функций-членов, не являющихся постоянными,
здесь недопустимо;
поэтому эту строку пришлось закомментировать.
45 -вызов функции SetWidth() для указателя pConstPtr.
Этот указатель объявлен как постоянный указатель на объект,
то есть он не может указывать ни на что, кроме объекта Rectangle,
который постоянным не является.(здесь указателю нельзя присвоить
другой адрес, но можно присвоить другое значение переменной на
которую он указывает)
При объявлении постоянного объекта указатель this автоматически
оказывается указателем на постоянный объект, а следовательно
использоваться может только с постоянными функциями-членами.
Более подробно о постоянных объектах и указателях в следующей главе.
Назад |
Начало урока |
Вверх |
Вперед
Паразитные дикие и зависшие указатели
Паразитные указатели называют еще дикими или зависшими указателями.
Не запускайте следующую программу, если только не хотите испытать удачу.
В этой программе намеренно создается паразиьный указатель.
typedef unsigned short int USHORT;
#include <iostream>
*pInt = 10;
std::cout << "*pInt: " << *pInt << std::endl;
delete pInt;
*pLong = 90000;
std::cout << "*pLong: " << *pLong << std::endl;
*pInt = 20; // Ой, ой он же удален!!
std::cout << "*pLong: " << *pLong << std::endl;
delete pLong;
return 0;
*pLong: 9000
*pInt: 20
*pLong: 65556
// Listing 8.9
// Demonstrates a stray pointer
#include <iostream>
Объявляем указатель на место под тип переменной в динамической памяти
и инициализируем (заносим в динамическую память значение) его.
*pInt = 10;
delete pInt;
*pLong = 90000;
std::cout << "*pLong: " << *pLong << std::endl;
*pInt = 20; // uh oh, this was deleted!
std::cout << "*pInt: " << *pInt << std::endl;
std::cout << "*pLong: " << *pLong << std::endl;
delete pLong;
return 0;
// Listing 8.9
// Demonstrates a stray pointer
#include <iostream>
*pInt = 10;
std::cout << "*pInt: " << *pInt << std::endl;
delete pInt;
*pLong = 90000;
std::cout << "*pLong: " << *pLong << std::endl;
//присваиваем указателю адрес нового места в динамической памяти
std::cout << "*pLong: " << *pLong << std::endl;
delete pLong;
return 0;
Указатели и константы
const int*pOne;
int*const pTwo;
const int*const pThree;
const int*p1;//указатель на константу типа int
int*const p2;//p2 константа, она не может указывать ни на что иное
Постоянные в качестве указателей и функций-членов
Rectangle();
~Rectangle();
int GetLength() const {
void SetWidth(int width) {
int itsLength;
int itsWidth;
itsLength = 10;
const Rectangle * pConstRect = new Rectangle;
Rectangle * const pConstPtr = new Rectangle;
<< " feet\n";
cout << "pConstRect width: " << pConstRect->GetWidth()
<< " feet\n";
cout << "pConstPtr width: " << pConstPtr->GetWidth()
<< " feet\n";
// pConstRect->SetWidth(10);
pConstPtr->SetWidth(10);
<< " feet\n";
cout << "pConstRect width: " << pConstRect->GetWidth()
<< " feet\n";
cout << "pConstPtr width: " << pConstPtr->GetWidth()
<< " feet\n";
return 0;
pRect width: 5 feet
pConstRect width: 5 feet
pConstPtr width: 5 feet
pRect width: 10 feet
pConstRect width: 5 feet
pConstPtr width: 10 feet
int GetWidth() const {
Строка 32 -объявляет указатель на объект класса Rectangle
Rectangle* pRect = new Rectangle;
const Rectangle * pConstRect = new Rectangle;
cout << "pRect width: " << pRect->GetWidth()
<< " feet\n";
cout << "pConstRect width: " << pConstRect->GetWidth()
<< " feet\n";
cout << "pConstPtr width: " << pConstPtr->GetWidth()
<< " feet\n";
pRect->SetWidth(10);
// pConstRect->SetWidth(10);
const Rectangle * pConstRect = new Rectangle;
void SetWidth(int width) {
pConstPtr->SetWidth(10);
Rectangle * const pConstPtr = new Rectangle;
Указатели const и this
Защищайте объекты доступные по ссылке ключевым словом
const если они не должны изменяться.
Объекты , подлежащие изменению, объявляйте без ключевого
слова const.
Небольшие объекты , которые не должны изменяться,
могут быть переданы как значения.
Не удаляйте один и тот же указатель дважды
Содержание