Бьерн Страуструп. Язык программирования С++ Второе дополненное издание



Pdf көрінісі
бет130/256
Дата11.07.2022
өлшемі2,87 Mb.
#37591
1   ...   126   127   128   129   130   131   132   133   ...   256
Байланысты:
Бьерн Страуструп. Язык программирования С . М Бином, 2011

6.7 Свободная память 
Если определить функции operator new() и operator delete(), управление памятью для класса можно 
взять в свои руки. Это также можно, (а часто и более полезно), сделать для класса, служащего базовым 
для многих производных классов. Допустим, нам потребовались свои функции размещения и 
освобождения памяти для класса employee ($$6.2.5) и всех его производных классов: 
class employee { 
// 
... 
public: 
void* 
operator 
new(size_t); 
void operator delete(void*, size_t); 
}; 
void* employee::operator new(size_t s) 

// отвести память в `s' байтов 
// и возвратить указатель на нее 

void employee::operator delete(void* p, size_t s) 

// `p' должно указывать на память в `s' байтов, 
// 
отведенную функцией employee::operator new(); 
// освободить эту память для повторного использования 

Назначение до сей поры загадочного параметра типа size_t становится очевидным. Это - размер 
освобождаемого объекта. При удалении простого служащего этот параметр получает значение 
sizeof(employee), а при удалении управляющего - sizeof(manager). Поэтому собственные функции 
классы для размещения могут не хранить размер каждого размещаемого объекта. Конечно, они могут 
хранить эти размеры (подобно функциям размещения общего назначения) и игнорировать параметр 
size_t в вызове operator delete(), но тогда вряд ли они будут лучше, чем функции размещения и 
освобождения общего назначения. 
Как транслятор определяет нужный размер, который надо передать функции operator delete()? Пока тип, 
указанный в operator delete(), соответствует истинному типу объекта, все просто; но рассмотрим такой 
пример: 
class manager : public employee { 
int 
level; 
// 
... 
}; 
void f() 

employee* p = new manager; // 
проблема 
delete 
p; 

В этом случае транслятор не сможет правильно определить размер. Как и в случае удаления массива, 
нужна помощь программиста. Он должен определить виртуальный деструктор в базовом классе 
employee: 
class employee { 
// 
... 
public: 
// 
... 
void* 
operator 
new(size_t); 


Бьерн Страуструп.
Язык программирования С++ 
 
175 
void operator delete(void*, size_t); 
virtual 
~employee(); 
}; 
Даже пустой деструктор решит нашу проблему: 
employee::~employee() { } 
Теперь освобождение памяти будет происходить в деструкторе (а в нем размер известен), а любой 
производный от employee класс также будет вынужден определять свой деструктор (тем самым будет 
установлен нужный размер), если только пользователь сам не определит его. Теперь следующий 
пример пройдет правильно: 
void f() 

employee* p = new manager; // 
теперь без проблем 
delete 
p; 

Размещение происходит с помощью (созданного транслятором) вызова 
employee::operator new(sizeof(manager)) 
а освобождение с помощью вызова 
employee::operator delete(p,sizeof(manager)) 
Иными словами, если нужно иметь корректные функции размещения и освобождения для производных 
классов, надо либо определить виртуальный деструктор в базовом классе, либо не использовать в 
функции освобождения параметр size_t. Конечно, можно было при проектировании языка 
предусмотреть средства, освобождающие пользователя от этой проблемы. Но тогда пользователь 
"освободился" бы и от определенных преимуществ более оптимальной, хотя и менее надежной 
системы. 
В общем случае, всегда есть смысл определять виртуальный деструктор для всех классов, которые 
действительно используются как базовые, т.е. с объектами производных классов работают и, возможно, 
удаляют их, через указатель на базовый класс: 
class X { 
// 
... 
public: 
// 
... 
virtual 
void 
f(); 
// в X есть виртуальная функция, поэтому 
// определяем виртуальный деструктор 
virtual 
~X(); 
}; 


Достарыңызбен бөлісу:
1   ...   126   127   128   129   130   131   132   133   ...   256




©emirsaba.org 2024
әкімшілігінің қараңыз

    Басты бет