Специально для любителей шОблоноф и прочих C++ наворотов. :) Сразу пердупердяю: задачка с подвохом.
Дано.
В программе есть некоторое количество классов, которые являются элементами односвязных списков с internal storage (проще говоря, это когда указатель на следующий элемент является членом класса).
Классы имеют вид:
Причем, варьируется не только название класса, но и название поля "nextListItem".
Задача.
Написать универсальную функцию вставки в начало списка с применением template, inline и pointer-to-member операторов. На входе должны быть: вставляемый элемент; указатель на первый элемент; название поля, указывающего на следующий элемент и тип класса. Функция может быть глобальной или членом namespace или функцией какого-либо нового класса - важно, чтобы она была одна, но работала для всех тех классов. Функция должна работать примерно так же, как ниже представленный #define:
Посчитать количество строк в полученной функции. Факультативно: перечислить и обосновать преимущества перед приведенным #define.
Дано.
В программе есть некоторое количество классов, которые являются элементами односвязных списков с internal storage (проще говоря, это когда указатель на следующий элемент является членом класса).
Классы имеют вид:
class MyClass...
{
MyClass *nextListItem;
Причем, варьируется не только название класса, но и название поля "nextListItem".
Задача.
Написать универсальную функцию вставки в начало списка с применением template, inline и pointer-to-member операторов. На входе должны быть: вставляемый элемент; указатель на первый элемент; название поля, указывающего на следующий элемент и тип класса. Функция может быть глобальной или членом namespace или функцией какого-либо нового класса - важно, чтобы она была одна, но работала для всех тех классов. Функция должна работать примерно так же, как ниже представленный #define:
#define INSERT_TO_LIST(firstItemPtr, item, nextItemName) \ (item)->nextItemName= (firstItemPtr), (firstItemPtr)= (item)
Посчитать количество строк в полученной функции. Факультативно: перечислить и обосновать преимущества перед приведенным #define.
Re: мм?
Date: 2009-07-19 07:21 pm (UTC)Re: мм?
Date: 2009-07-19 07:26 pm (UTC)По-твоему ее можно решить? (не говори пока решение, если ты его знаешь)
Re: мм?
Date: 2009-07-19 07:40 pm (UTC)Re: мм?
Date: 2009-07-19 08:11 pm (UTC)Re: мм?
Date: 2009-07-19 08:36 pm (UTC)Да, тело класса, конечно, придется курочить так или иначе. Лично для себя я сделал так:
template <class Type> class ListHelper { public: static inline void insert(Type *val, Type *&first, Type * Type::*next) { val->*next = first; first = val; } };В самом классе MyClass приходится использовать friend:
class MyClass... { friend class ListHelper<MyClass>>; MyClass *nextItem;Ну и вызов так:
Позитив этого подхода -
1. Сакральное поле nextItem по-прежнему закрыто от некорректного использования.
2. В ListHelper можно написать еще кучу функций для работы со списком, и под каждую новую функцию уже не надо будет писать "friend" и модифицировать MyClass.
Re: мм?
Date: 2009-07-20 07:24 am (UTC)#include <iostream> template <class T> inline T* unshift(T* &list, T* item, T* T::*member) { item->*member = list; return list = item; } template <class T> struct helper { static inline void insert(T *val, T *&first, T *T::*next) { val->*next = first; first = val; } }; class item { item* next; friend class helper<item>; public: item(): next(0) {} item* getNext() { return next; } void prependList(item* &list) { unshift(list, this, &item::next); } }; int main() { item *a = new item(); item *b = new item(); b->prependList(a); // OK a = new item(); b = new item(); helper<item>::insert(a, b, &item::next); // G++ 3.4.5. error: `item*item::next' is private // MSVC 9. error C2248: 'item::next' : cannot access private member declared in class 'item' }Что-то не то.
> А если оставишь ее внешней, то получится, что ты обращаешься из функции класса к внешней функции unshift которая уже в свою очередь пытается обратиться к приватному полю member класса.
Внешняя функция unshift пытается обратиться не к приватному полю, а к некому (по указателю). Права на доступ к полю, чтоб создать указатель на него, нужны только создателю указателя (в данном случае пользователю unshift, item::prependList()).
По этой же причине твой код не работает: для вызывающего кода такого понятия, как MyClass::nextItem, не существует. Контроль доступа ведь производится на уровне имен (это не Ява какая-нибудь).
Так что я не могу придумать решения, требующее только подружить класс item с кем-нибудь. Указатель на next по-любому приходится создавать внутри item.
Re: мм?
Date: 2009-07-20 07:47 am (UTC)Re: мм?
Date: 2009-07-20 09:02 am (UTC)- тут надо struct вместо class
В данном случае main должна быть объявлена friend класса item. Так что приватность все-таки срабатывает, но не там, где я думал. У меня оно работает по стечению обстоятельств - то поля public, то вызов из самого класса item. :) Надо будет придумать что-нибудь по-лучше на тот случай, когда перестанет работать.
Re: мм?
Date: 2009-07-20 09:29 am (UTC)Единственный разумный вариант, который потребует от item только дружбы — писать специализации функции а-ля:
template T*T::* getNext(); template<> item*item::* getNext() { return &item::next; }Но это уже онанизм очень похожий на вариант юзера dii.
Re: мм?
Date: 2009-07-20 09:37 am (UTC)