|
||||
|
Шаг 27 - Умные указатели. Перегрузка operator*, operator(),operator-›*. Пробегая по верхам интересных идиом я упустил одну важную вещь. Поначалу она была не так важна, но пришло время замучать и ее. Я имею в виду то, что наши замечательно умные указатели, smart pointers, вообще-то имеют неполную семантику. То есть, они не полностью имитируют обычные, настоящие указатели. За примерами не надо ходить далеко - попробуем разыменовать смарт или вызвать функцию по указателю: obj = *(smart_ptr); (obj-›*ptr_to_funct) (some_parameter); С первой проблемой рассчитаться легко. Если Вы НЕ читаете сейчас этот Шаг, не беспокойтесь - решение придет само, в тот момент, когда задача возникнет. //Ясно, это реализации перегруженных операторов-селекторов. CSmth* operator-›() const { return prt_real; } CSmth& operator* () const { return *ptr_real; } operator CSmth* () const { return ptr_real; } А что вторая проблема? Да, тут ситуация намного серьезнее, и если Вы опять-таки не читаете этот Шаг, то нужно немедленно прочесть его - или первоисточник - статью Мейерса в Dr. Dobb's Journal. Только там придется продираться через тучные стада шаблонов и долгих рассуждений. Без шаблона конечно не обойтись, но нужно ухватить хотя бы идею. Поэтому сделаем так, как нормальный человек читает детективы Марининой: первые и последние две страницы. Сначала, кто такой operator-›*. Это который вызывает функцию-член по указателю. Такую функцию нужно вызывать с указанием объекта, если из другой функции-члена, то в виде (this-›*mpf)() или (*this).*mpf(). // Этот класс используется так же дальше class CSmth { public: int a; int pf (void) {return a;} }; typedef int (CSmth::*PF)(void); Если мы нарисуем умный указатель на объект класса CSmth, определять operator-›*() нужно самостоятельно. Что он должен вернуть? Нечто такое, к чему можно применить operator(). То есть, это снова proxy-объект. Мейерс называет его "незавершенный вызов функции-члена" (Pending Member Function Calling). Он должен знать, к какому объекту применяется, и знать об указателе на функцию, то есть он должен иметь в себе указатели на них обоих, и инициализировать их в конструкторе. А operator() должен возвращать уже нужный нам int, или все что угодно другое, что может вернуть указываемая функция. // класс незавершенного вызова. Это самое важное. class pmfc { private: // два указателя - на объект и на функцию CSmth* m_smth; PF m_pfunct; public: // конструктор pmfc (CSmth*& _smth, PF& _pfunct) : m_smth(_smth), m_pfunct(_pfunct) {} // вызов конечной функции из оператора () int operator()() const { return (m_smth-›*m_pfunct)(); } }; // класс умного указателя. class CPtr { private: CSmth* a; public: CPtr() { a = new CSmth(); } ~CPtr() { delete a; } CSmth* operator-›() const { return a; } CSmth& operator* () const { return *a; } operator CSmth* () const { return a; } // возвращает PMFC. Это тоже важно. pmfc operator-›*(PF _pf) { return pmfc (a, _pf); } }; // проверим все int main() { CPtr t; t-›a = 10; // заодно проверим operator* (*t).a = 16; int b = 0; // получили указатель на функцию. PF lpF =&CSmth::pf; // вызвали функцию по указателю при помощи нашей конструкции b = (t-›*lpF)(); return 0; } С тоской взглянув на полученный результат, сразу осознаешь, что без шаблонов не обойтись - ведь нужно обслуживать разные типы указателей на функции. Но зато мы минимум знаем, как решать эту проблему. Еще раз испытали proxy-объекты. Потрогали указатели на функции и функции члены. Перегрузили операторы * и (). И если встанет проблема - то знаем, где искать решение (у Скотта Мейерса). |
|
||
Главная | Контакты | Нашёл ошибку | Прислать материал | Добавить в избранное |
||||
|