LINUX.ORG.RU

friend class для интрузивных контейнеров

 


0

1

Где-то я читал, что friend class это плохо для инкапсуляции. Но мне потребовалось сделать, чтобы class Task; мог входить в разные интрузивные списки:

class RunningQueue;
class WaitingQueue;
class LocalQueue;
В результате получилось:
class Task {
//...
private:
    friend class Scheduler;
    friend class RunningQueue;
    friend class WaitingQueue;
    friend class LocalQueue;

    Task*      m_next = nullptr;
//...
};
Делать public Task* m_next; совсем нехорошо, потому что хочется контролировать, кто это поле меняет.

Можно ли тут как-то сделать лучше (чтобы и без friend и инкапсуляция осталась).

P.S. Список интрузивный, потому что lockfree, да и каждый раз при перенесении Task* из одного списка в другой заниматься работой с памятью ни к чему.

P.P.S. Как добавить тег lockfree на LOR?



Последнее исправление: prefetch (всего исправлений: 1)
Ответ на: комментарий от annulen

Это 4.2, все наоборот

Каким образом возможность дружественного класса получить доступ к приватным полям улучшает инкапсуляцию?

m0rph ★★★★★
()
Ответ на: комментарий от m0rph

Посмотри идиомы passkey и attorney-client

Спасибо за ссылки. passkey забавная идиома - мозг выворачивает, но, возможно, где-то ее можно применить.

prefetch
() автор топика
class Task: public Queue<Task, Running>
anonymous
()
Ответ на: комментарий от no-such-file

Таким, что эти поля не приходится делать публичными и т.о. потерять контроль над доступом вообще.

Зато все дружбаны класса имеют возможность поломать инвариант. Да, это лучше, чем объявлять поля публичными, но это НЕ улучшает инкапсуляцию, не стоит подменять понятия.

m0rph ★★★★★
()
Ответ на: комментарий от prefetch

Спасибо за ссылки. passkey забавная идиома - мозг выворачивает, но, возможно, где-то ее можно применить.

На мой взгляд как раз идиома passkey простая, как бревно. Есть класс-ключ с приватным конструктором, который могут создавать только избранные. Есть функция, принимающая такой объект в качестве одного из аргументов. Соответственно вызвать функцию можно только из методов классов, которые имеют возможность создать объект-ключ.

m0rph ★★★★★
()
Ответ на: комментарий от m0rph

это лучше, чем объявлять поля публичными, но это НЕ улучшает инкапсуляцию

Если другого выхода нет, и все остальные варианты хуже, то этот вариант, по сравнению с другими, улучшает инкапсуляцию. Хреновая инкапсуляция лучше чем никакая.

no-such-file ★★★★★
()
Ответ на: комментарий от m0rph

На мой взгляд как раз идиома passkey простая, как бревно.

Простая, но немного хитровывернутая.

P.S. Аргумент Passkey<Someclass>() полезно сделать аргументом по умолчанию, если метод с таким аргументом единственный.

prefetch
() автор топика
Ответ на: комментарий от no-such-file

очевидно же

T operator +(const T &lhs, const T &rhs) {
    T result(lhs);
    return result += rhs;
}

за всё остальное руки отпиливаются ржавой ножовкой.

anonymous
()
Ответ на: комментарий от prefetch

P.S. Аргумент Passkey<Someclass>() полезно сделать аргументом по умолчанию, если метод с таким аргументом единственный.

Это не сработает. Хотя в C++11 ты можешь вместо «Passkey<Someclass>()» писать просто «{}» когда передаешь аргумент.

m0rph ★★★★★
()

Где-то я читал, что friend class это плохо для инкапсуляции

Переходи уже на следующий уровень — приучайся забивать болт на такие вот «антипаттерны» и думать своей головой.

anonymous
()
Ответ на: комментарий от no-such-file

обоснуй

можно и так сделать:

T operator +(T lhs, const T &rhs) {
    lhs += rhs;
    return lhs;
}
но я написал чтобы и нубам было понятно.

anonymous
()
Ответ на: обоснуй от anonymous

Не позорься, балбес, хватит копипастить из учебника - ты ж не понимаешь, что пишешь. 42 и obj разные типы, а у тебя в сигнатуре функция принимает _один_ тип.

Ну и вообще

lhs += rhs
это просто обхохочешься. Давай представим на минутку, что мы обсуждаем не хеловорд, а нормальную программу, где операция + должна сделать что-то осмысленное, т.е. оперировать с кишками класса T. Ты даже но понял вообще, что конкретно + тут просто для примера.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

т.е. звон ты где-то слышал...

какой же ты тугой. чтобы самостоятельно убедиться в этом — напиши вариант с friend который работает.

если результат a = a + b отличается от a += b, то разработчику отпиливают руки описанным выше способом. если же тебе нужна оптимизация (умножение матриц, например), то придётся забыть об операторах со встроенными типами слева и перегружать в соответствующих классах. перегружать и так придётся, поскольку сложные operator + ещё имеют свойство терять по дороге коммутативность.

anonymous
()
Ответ на: т.е. звон ты где-то слышал... от anonymous

если результат a = a + b

Специально для тебя повторяю, + тут только для примера. Ну ок, пусть будет operation|, или >>.

умножение матриц, например

В твои олень мозги не приходит идея, что операторы могут иметь произвольную семантику, далёкую от математики? К примеру я могу использовать + для того, чтобы добавлять числа в вектор. 42+vec добавляет с начала вектора, vec+42 с конца. Ну и где теперь твой a+=b?

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Ну ок, пусть будет operation|, или >>

да, это касается любых бинарных операторов. уже задумались над тем как пофиксить, но к 17-му стандарту имхо не успеют.

К примеру я могу использовать + для того, чтобы добавлять числа в вектор

...в свой последний день на работе...

если так не терпится, то можно так:

template <typename Another> T operator +(const Another &lhs, const T &rhs) {
	return rhs.Add(lhs);
}
некоммутативную (как в твоём упоротом примере) версию сам догадаешься как сделать, надеюсь...

если ты неосилил кресты — это не повод кукарекать об этом на весь лор. это не такое уж и большое достижение: процентов 80 тел, которые получают зарплату за писание крестокода, тоже неосилили, не говоря уже о всяких мимокрокодилах.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.