Доступ к базовым классам

Подобно члену базисный класс можно обрисовать как личный, защищенный либо общий: class X { public: int a; // ... }; class Y1 : public X { }; class Y2 : protected X { }; class Y3 : private X { }; Так как X - общий базисный класс для Y1, в хоть какой функции, если есть необходимость, можно (неявно) конвертировать Y1* в X Доступ к базовым классам*, и притом в ней будут доступны общие члены класса X: void f(Y1* py1, Y2* py2, Y3* py3) { X* px = py1; // нормально: X - общий базисный класс Y1 py1->a = 7; // нормально px = py2; // ошибка: X - защищенный базисный класс Y2 py2->a = 7; // ошибка px = py3; // ошибка: X - личный базисный класс Y Доступ к базовым классам3 py3->a = 7; // ошибка } Сейчас пусть описаны class Y2 : protected X { }; class Z2 : public Y2 { void f(); }; Так как X - защищенный базисный класс Y2, только друзья и члены Y2, также друзья и члены всех производных от Y2 классов (а именно Z2) могут по мере надобности преобразовывать (неявно) Y2* в X*. Не считая того Доступ к базовым классам они могут обращаться к общим и защищенным членам класса X: void Z2::f(Y1* py1, Y2* py2, Y3* py3) { X* px = py1; // нормально: X - общий базисный класс Y1 py1->a = 7; // нормально px = py2; // нормально: X - защищенный базисный класс Y2, // а Z2 - производный класс Y2 py2->a = 7; // нормально Доступ к базовым классам px = py3; // ошибка: X - личный базисный класс Y3 py3->a = 7; // ошибка } В конце концов, разглядим: class Y3 : private X { void f(); }; Так как X - личный базисный класс Y3, только друзья и члены Y3 могут по мере надобности преобразовывать (неявно) Y3* в X*. Не считая того они могут обращаться к общим и Доступ к базовым классам защищенным членам класса X: void Y3::f(Y1* py1, Y2* py2, Y3* py3) { X* px = py1; // нормально: X - общий базисный класс Y1 py1->a = 7; // нормально px = py2; // ошибка: X - защищенный базисный класс Y2 py2->a = 7; // ошибка px = py3; // нормально: X - личный базисный класс Y3, // а Y3::f член Доступ к базовым классам Y3 py3->a = 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); 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(); };
dostoevskij-f-m-hristianskie-motivi-v-proze-19-veka-sochinenie.html
dostoevskij-f-m-istoki-tvorchestva-f-m-dostoevskogo-sochinenie.html
dostoevskij-f-m-mir-unizhennih-i-oskorblennih-v-romane-f-m-dostoevskogo-prestuplenie-i-nakazanie-sochinenie.html