Дружественные классы
От: nikov США http://www.linkedin.com/in/nikov
Дата: 25.04.07 14:51
Оценка:
Дружественные (friend) классы — полезная ли это концепция? И почему в .NET от нее отказались?
Re: Дружественные классы
От: FDSC Россия consp11.github.io блог
Дата: 25.04.07 15:31
Оценка: -1 :)
Здравствуйте, nikov, Вы писали:

N>Дружественные (friend) классы — полезная ли это концепция? И почему в .NET от нее отказались?


Она только запутывает архитектуру приложения, делает большей частных связей между классами, усложняя её понимание. С другой стороны, без дружественных классов можно обойтись, как и без множественного наследования.

Вообще, говорят, использование дружественных классов является признаком плохой архитектуры приложения
Re[2]: Дружественные классы
От: SergH Россия  
Дата: 25.04.07 16:06
Оценка: +1
Здравствуйте, FDSC, Вы писали:

FDS>Она только запутывает архитектуру приложения, делает большей частных связей между классами, усложняя её понимание. С другой стороны, без дружественных классов можно обойтись, как и без множественного наследования.


Точно. И вообще без наследования, кстати, можно обойтись. Отличные есть языки программирования без всего этого — С, например.
(не, я понимаю, что наследование всё-таки полезнее дружбы, просто, имхо, подобные аргументы критики не выдерживают)

FDS>Вообще, говорят, использование дружественных классов является признаком плохой архитектуры приложения


Врут. Не верьте им.

Признаком плохой архитектуры является использование дружественных классов неправильно. То же можно сказать про множественное наследование, обычное наследование, функции, макросы и т.п.

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

Хотя, есть еще "методическая" сторона. Если в 99% процентах случаев на практике возможность используется именно неправильно, возможно, стоит выкинуть её из языка. Но тогда я начал бы всё же с goto
Делай что должно, и будь что будет
Re[3]: Дружественные классы
От: AndreiF  
Дата: 25.04.07 16:21
Оценка:
Здравствуйте, SergH, Вы писали:

В C# они эмулируются при помощи модификатора internal и вынесения классов в отдельную сборку. А вообще, исключение дружественных классов — это дурь проектировщиков шарпа, ИМХО.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[4]: Дружественные классы
От: FDSC Россия consp11.github.io блог
Дата: 25.04.07 17:05
Оценка: +1 -1
Здравствуйте, AndreiF, Вы писали:

AF>Здравствуйте, SergH, Вы писали:


AF>В C# они эмулируются при помощи модификатора internal и вынесения классов в отдельную сборку. А вообще, исключение дружественных классов — это дурь проектировщиков шарпа, ИМХО.


Ну, я бы сказал, у меня, когда я начинал изучать C# ощущение было очень простое: "всё, что есть в Delphi — есть в C#, остальное решили не делать"
Re[5]: Дружественные классы
От: Igor Trofimov  
Дата: 25.04.07 18:53
Оценка: +1
FDS>Ну, я бы сказал, у меня, когда я начинал изучать C# ощущение было очень простое: "всё, что есть в Delphi — есть в C#, остальное решили не делать"

Ну, во-первых не все, а во-вторых, не только это
Re[6]: Дружественные классы
От: FDSC Россия consp11.github.io блог
Дата: 25.04.07 19:05
Оценка:
Здравствуйте, Igor Trofimov, Вы писали:

FDS>>Ну, я бы сказал, у меня, когда я начинал изучать C# ощущение было очень простое: "всё, что есть в Delphi — есть в C#, остальное решили не делать"


iT>Ну, во-первых не все, а во-вторых, не только это


Ну, я же говорю про впечатление. Конечно не только. Мне это впечатление очень мешало первое время: различий-то там много
Re: Дружественные классы
От: Константин Л.  
Дата: 25.04.07 20:05
Оценка:
Здравствуйте, nikov, Вы писали:

N>Дружественные (friend) классы — полезная ли это концепция? И почему в .NET от нее отказались?


1. иногда полезная, особенно для nested classes
2. может заимплементить сложно было? Хотя навскидку не придумал почему
Re: Дружественные классы
От: sharcUs Беларусь http://sharcus.blogspot.com/
Дата: 26.04.07 05:53
Оценка:
N>Дружественные (friend) классы — полезная ли это концепция? И почему в .NET от нее отказались?

С. Макконнелл, Совершенный код. 2-е издание

Избегайте использования дружественных классов
Иногда — например, при реализации шаблона Состояние (State) — дисциплинированное использование дружественных классов помогает управлять сложностью (Gamma et al., 1995).
Однако обычно дружественные классы нарушают инкапсуляцию. Они увеличивают объем кода, о котором приходится думать в каждый конкретный момент времени, повышая тем самым сложность программы.


Полностью солидарен с автором книги в этом вопросе.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[2]: Дружественные классы
От: Zuka Россия  
Дата: 27.04.07 02:42
Оценка: +1
Здравствуйте, sharcUs, Вы писали:

U>С. Макконнелл, Совершенный код. 2-е издание


U>

U>Избегайте использования дружественных классов
U>Иногда — например, при реализации шаблона Состояние (State) — дисциплинированное использование дружественных классов помогает управлять сложностью (Gamma et al., 1995).
U>Однако обычно дружественные классы нарушают инкапсуляцию. Они увеличивают объем кода, о котором приходится думать в каждый конкретный момент времени, повышая тем самым сложность программы.


U>Полностью солидарен с автором книги в этом вопросе.


Если не использовать френдов, то вместо того чтобы показать внутренности одному классу, придется так или иначе показать их всем. В результате объем кода, "о котором приходится думать в каждый конкретный момент времени" возрастает на порядки. Т.о. френды как раз таки способствуют инкапсуляции.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[3]: Дружественные классы
От: sharcUs Беларусь http://sharcus.blogspot.com/
Дата: 27.04.07 05:51
Оценка: +2 -1
Здравствуйте, Zuka, Вы писали:

Z>Здравствуйте, sharcUs, Вы писали:


Z>Если не использовать френдов, то вместо того чтобы показать внутренности одному классу, придется так или иначе показать их всем. В результате объем кода, "о котором приходится думать в каждый конкретный момент времени" возрастает на порядки. Т.о. френды как раз таки способствуют инкапсуляции.


В БОЛЬШИНСТВЕ случаев необходимость использования друзей свидетельствует о неправильном архитектурном решении, приводящем к усложнению объектной модели проекта и его дальнейшем сопровождении. К примеру, у меня НИ РАЗУ не появилась потребность в их использовании, хотя в прошлом уже не один завершенный проект, как на С++ так и на С#. Как правило в 98% процентах случаев включение и наследование вполне достаточно для решения требуемых задач.
Вместе с тем я не отрицаю, что в исключительных случаях использование друзей является ЕДИНСТВЕННО ВОЗМОЖНЫМ решением, однако ДЕЙСТВИТЕЛЬНАЯ необходимость их использования ограничивается единичными случаями, как и говорит Стив в приведенной мной цитате выше. Как правило потребность в дружественных сущностях является ОШИБКОЙ в архитектуре программной модели, от которой следует избавляться и использовать вместо нее другие подходы.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[4]: Дружественные классы
От: Курилка Россия http://kirya.narod.ru/
Дата: 27.04.07 05:58
Оценка:
Здравствуйте, sharcUs, Вы писали:

U>В БОЛЬШИНСТВЕ случаев необходимость использования друзей свидетельствует о неправильном архитектурном решении, приводящем к усложнению объектной модели проекта и его дальнейшем сопровождении. К примеру, у меня НИ РАЗУ не появилась потребность в их использовании, хотя в прошлом уже не один завершенный проект, как на С++ так и на С#. Как правило в 98% процентах случаев включение и наследование вполне достаточно для решения требуемых задач.

Есть ещё другой вариант — использовать интерфейсы, выдаваемые "по требованию". Скажем, есть параметр, и всем только читать его могут и лишь "избранные" писать, делаем отдельный интерфейс "сеттерный" и функцию получения его. Связность получается более прослеживаемая, и добавляется большая расширяемость (т.к. для новых "сеттеров" не надо будет править исходный класс, добавляя в него френдов)
Re: Дружественные классы
От: VladD2 Российская Империя www.nemerle.org
Дата: 27.04.07 08:41
Оценка: +7
Здравствуйте, nikov, Вы писали:

N>Дружественные (friend) классы — полезная ли это концепция? И почему в .NET от нее отказались?


На мой взгляд — полезная. А в дотнете очень могого нет по сугубо личностным соображениям его создателей и по причинам неудобности реализации (ну, типа это потребует много сил, так что ну его на).

Другое дело, что вообще, на мой взгляд проблемы инкапсуляции несколько преувеличены. Да и большинство проблем с инкапсуляцией появляются вовсе не из-за ограничений языка/среды, а в следствии невнимательности разработчиков.

ЗЫ

Кстати, умнее было бы сделать не дружественные классы, а дружественные интерфесы или даже дружественный список методов. Ведь для взаимодействия свяазнных типов порой достаточно одного-двух методов, а классу открываются все потроха своего друга, что только усложняет поддержку кода.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Дружественные классы
От: alzt  
Дата: 27.04.07 09:45
Оценка:
Здравствуйте, sharcUs, Вы писали:

U>В БОЛЬШИНСТВЕ случаев необходимость использования друзей свидетельствует о неправильном архитектурном решении, приводящем к усложнению объектной модели проекта и его дальнейшем сопровождении. К примеру, у меня НИ РАЗУ не появилась потребность в их использовании, хотя в прошлом уже не один завершенный проект, как на С++ так и на С#. Как правило в 98% процентах случаев включение и наследование вполне достаточно для решения требуемых задач.

U>Вместе с тем я не отрицаю, что в исключительных случаях использование друзей является ЕДИНСТВЕННО ВОЗМОЖНЫМ решением, однако ДЕЙСТВИТЕЛЬНАЯ необходимость их использования ограничивается единичными случаями, как и говорит Стив в приведенной мной цитате выше. Как правило потребность в дружественных сущностях является ОШИБКОЙ в архитектуре программной модели, от которой следует избавляться и использовать вместо нее другие подходы.

class O
{
};

class I
{
};

class C
{
public:
    I GetIterator();
private:
    vector<O> m_elems;
    int Count();
    O GetElem(int index);
};


Я хочу, чтобы класс C предоставлял итератор. При этом детали (Count и GetElem) раскрывать не хочу.
Всё должно быть гибко — у меня может быть несколько итераторов на все случаи жизни (фабрики классов и т.п.).
Т.е. в одном случае обход по порядку, во втором обратный, а в третьем вообще через один или по маске. Т.е. жёстко задать Count и GetItem нельзя.

Вопрос: как мне это реализовать без friend?
Re: Дружественные классы
От: vdimas Россия  
Дата: 27.04.07 10:31
Оценка: +1
Здравствуйте, nikov, Вы писали:

N>Дружественные (friend) классы — полезная ли это концепция? И почему в .NET от нее отказались?



Не совсем отказались, вот навскидку пара аналогов:

1. В .Net есть способ ограничение видимости в пределах сборки, (internal в C#), соответственно internal класс, или internal члены публичных классов являются дружественными всем классам сборки.

2. Вложенные классы по-умолчанию являются дружественными объемлющему классу.


В базовом фреймворке процент использования этой "концепции" зашкаливает за 50%, по моему субъективному наблюдению. Т.е. кол-во internal типов не уступает кол-ву публичных типов.
Re[5]: Дружественные классы
От: igna Россия  
Дата: 27.04.07 10:46
Оценка: +1
Здравствуйте, alzt, Вы писали:

A>Вопрос: как мне это реализовать без friend?


Покажи, как ты это реализуешь с friend-ом.
Re[2]: Дружественные классы
От: Sinclair Россия https://github.com/evilguest/
Дата: 27.04.07 12:11
Оценка:
Здравствуйте, vdimas, Вы писали:
V>В базовом фреймворке процент использования этой "концепции" зашкаливает за 50%, по моему субъективному наблюдению. Т.е. кол-во internal типов не уступает кол-ву публичных типов.
Гм. Не совсем так: интернальность классов никакого отношения к "дружбе" не имеет. Классы внутри сборки не имеют никакого особого доступа к членам интернал-классов.
А интернал поля (которые были бы аналогом friend+private) применяются гораздо-гораздо реже.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[6]: Дружественные классы
От: alzt  
Дата: 27.04.07 12:26
Оценка:
Здравствуйте, igna, Вы писали:

A>>Вопрос: как мне это реализовать без friend?

I>Покажи, как ты это реализуешь с friend-ом.

class O
{
};

class I;

class C
{
public:
    I GetIterator();
private:
    vector<O> m_elems;
    int Count();
    O GetElem(int index);

    friend class I;
};

class I
{
public:
    void Reset()=0;
    bool IsNext(){=0;
    O Current()=0;
protected:
    int Count(){return c->Count();}
    O GetElem(int index){return c->GetElem(index);}
private:
    C* c;
};


Реализация конкретного итератора:

class I1 : I
{
public:
    I1():m_index(0){}
    void Reset(){m_index = 0;}
    bool IsNext(){return m_index<I::Count();}
    O Current(){return I::GetElem(m_index);}
private:
    int m_index;
};


В итоге о внутренней структуре знает только класс I, и немного его наследники.
Остальные знают только об интерфейсе итератора.
Re[7]: Дружественные классы
От: alzt  
Дата: 27.04.07 12:32
Оценка:
Здравствуйте, alzt, Вы писали:

Забыл virtual подставить :

public:
    virtual void Reset()=0;
    virtual bool IsNext(){=0;
    virtual O Current()=0;
Re[7]: Дружественные классы
От: FDSC Россия consp11.github.io блог
Дата: 27.04.07 13:26
Оценка: +1
Здравствуйте, alzt, Вы писали:

A>В итоге о внутренней структуре знает только класс I, и немного его наследники.

A>Остальные знают только об интерфейсе итератора.

А в чём преимущество этой архитектуры по сравнению с реализацией итератора прямо в C и его наследниках?
Т.е. вопрос, зачем вы разделяете класс I и класс C? Почему не перенести функциональность класса I в класс C?
Re[8]: Дружественные классы
От: Курилка Россия http://kirya.narod.ru/
Дата: 27.04.07 13:30
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Т.е. вопрос, зачем вы разделяете класс I и класс C? Почему не перенести функциональность класса I в класс C?


+ сделать публичный интерфейс, а доступ к внутреннему классу I закрыть (а то так можно создать экземпляр итератора без коллекции, это обходится, но зачем добавлять костыли?).
Re[8]: Дружественные классы
От: alzt  
Дата: 27.04.07 14:03
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Здравствуйте, alzt, Вы писали:


A>>В итоге о внутренней структуре знает только класс I, и немного его наследники.

A>>Остальные знают только об интерфейсе итератора.

FDS>А в чём преимущество этой архитектуры по сравнению с реализацией итератора прямо в C и его наследниках?

FDS>Т.е. вопрос, зачем вы разделяете класс I и класс C? Почему не перенести функциональность класса I в класс C?

Такая ситуация: один экземпляр класса C. Необходимо обеспечить его обход в двух потоках.
Если использовать один класс C, то придётся его копировать, что не всегда хорошо.
Re[9]: Дружественные классы
От: FDSC Россия consp11.github.io блог
Дата: 27.04.07 14:07
Оценка:
Здравствуйте, alzt, Вы писали:

A>Здравствуйте, FDSC, Вы писали:


FDS>>Здравствуйте, alzt, Вы писали:


A>>>В итоге о внутренней структуре знает только класс I, и немного его наследники.

A>>>Остальные знают только об интерфейсе итератора.

FDS>>А в чём преимущество этой архитектуры по сравнению с реализацией итератора прямо в C и его наследниках?

FDS>>Т.е. вопрос, зачем вы разделяете класс I и класс C? Почему не перенести функциональность класса I в класс C?

A>Такая ситуация: один экземпляр класса C. Необходимо обеспечить его обход в двух потоках.

A>Если использовать один класс C, то придётся его копировать, что не всегда хорошо.

С какой стати его придётся копировать?
Re[8]: Дружественные классы
От: igna Россия  
Дата: 27.04.07 14:09
Оценка:
Здравствуйте, alzt, Вы писали:

A>Забыл virtual подставить :


A>public:
A>    virtual void Reset()=0;
A>    virtual bool IsNext(){=0;
A>    virtual O Current()=0;


Спасибо, но и с этими изменениями не компилируется. Если можно дай компилируемый вариант.
Re[9]: Дружественные классы
От: alzt  
Дата: 27.04.07 14:19
Оценка:
Здравствуйте, igna, Вы писали:

I>Спасибо, но и с этими изменениями не компилируется. Если можно дай компилируемый вариант.


class O
{
};

class I;

class C
{
public:
    I GetIterator();
private:
    vector<O> m_elems;
    int Count();
    O GetElem(int index);

    friend class I;
};

class I
{
public:
    virtual void Reset()=0;
    virtual bool IsNext()=0;
    virtual O Current()=0;
protected:
    int Count(){return c->Count();}
    O GetElem(int index){return c->GetElem(index);}
private:
    C* c;
};

class I1 : I
{
public:
    I1():m_index(0){}
    void Reset(){m_index = 0;}
    bool IsNext(){return m_index<I::Count();}
    O Current(){return I::GetElem(m_index);}
private:
    int m_index;
};
Re[10]: Дружественные классы
От: alzt  
Дата: 27.04.07 14:21
Оценка:
Здравствуйте, FDSC, Вы писали:

A>>Такая ситуация: один экземпляр класса C. Необходимо обеспечить его обход в двух потоках.

A>>Если использовать один класс C, то придётся его копировать, что не всегда хорошо.

FDS>С какой стати его придётся копировать?


Объект класса C предположим имеет скрытую переменную m_index.
Тогда, если использовать один объект (ссылки, указатели на него), то разные потоки будут его изменять неопределённо.
Я не смогу гарантировать, что m_index при обходе не будет изменён где-то ещё.
Re[11]: Дружественные классы
От: FDSC Россия consp11.github.io блог
Дата: 27.04.07 14:23
Оценка:
Здравствуйте, alzt, Вы писали:

A>Здравствуйте, FDSC, Вы писали:


A>>>Такая ситуация: один экземпляр класса C. Необходимо обеспечить его обход в двух потоках.

A>>>Если использовать один класс C, то придётся его копировать, что не всегда хорошо.

FDS>>С какой стати его придётся копировать?


A>Объект класса C предположим имеет скрытую переменную m_index.

A>Тогда, если использовать один объект (ссылки, указатели на него), то разные потоки будут его изменять неопределённо.
A>Я не смогу гарантировать, что m_index при обходе не будет изменён где-то ещё.

Кто мешает прямо в классе создавать разные контексты поиска для разных потоков?
Re[8]: Дружественные классы
От: alzt  
Дата: 27.04.07 14:26
Оценка:
Здравствуйте, FDSC, Вы писали:

A>>В итоге о внутренней структуре знает только класс I, и немного его наследники.

A>>Остальные знают только об интерфейсе итератора.

FDS>А в чём преимущество этой архитектуры по сравнению с реализацией итератора прямо в C и его наследниках?

FDS>Т.е. вопрос, зачем вы разделяете класс I и класс C? Почему не перенести функциональность класса I в класс C?

А если мне потребуется ещё обход I2, скажем такой, чтобы объекты типа O перебирались по алфавиту, или только такие, которые имеют какое-то свойство, а остальные пропускались.
Тогда вся логика обхода будет сосредоточена в одном классе, а от класса C мне потребуется только вернуть нужный вид обхода.
Если же перенести функциональность класса I в класс C, то менять будет сложнее.
Re[9]: Дружественные классы
От: FDSC Россия consp11.github.io блог
Дата: 27.04.07 14:31
Оценка:
Здравствуйте, alzt, Вы писали:

A>Здравствуйте, FDSC, Вы писали:


A>>>В итоге о внутренней структуре знает только класс I, и немного его наследники.

A>>>Остальные знают только об интерфейсе итератора.

FDS>>А в чём преимущество этой архитектуры по сравнению с реализацией итератора прямо в C и его наследниках?

FDS>>Т.е. вопрос, зачем вы разделяете класс I и класс C? Почему не перенести функциональность класса I в класс C?

A>А если мне потребуется ещё обход I2, скажем такой, чтобы объекты типа O перебирались по алфавиту, или только такие, которые имеют какое-то свойство, а остальные пропускались.

A>Тогда вся логика обхода будет сосредоточена в одном классе, а от класса C мне потребуется только вернуть нужный вид обхода.
A>Если же перенести функциональность класса I в класс C, то менять будет сложнее.


Нет.
1. Можно сделать наследников класса C
или
2. Достаточно передать в сам класс C делегат (указатель на функцию) сравнения элементов, как это делается в стандартной реализации qsort
То же можно задать и для фильтра.

Т.е., вообще говоря, тип обхода и фильтра можно изменить независимо от самого кода обхода, например, даже другим классом, если очень хочется
Re[10]: Дружественные классы
От: alzt  
Дата: 27.04.07 14:38
Оценка:
Здравствуйте, FDSC, Вы писали:

A>>А если мне потребуется ещё обход I2, скажем такой, чтобы объекты типа O перебирались по алфавиту, или только такие, которые имеют какое-то свойство, а остальные пропускались.

A>>Тогда вся логика обхода будет сосредоточена в одном классе, а от класса C мне потребуется только вернуть нужный вид обхода.
A>>Если же перенести функциональность класса I в класс C, то менять будет сложнее.


FDS>Нет.

FDS>1. Можно сделать наследников класса C
Вариант плохой, т.к. при наличии других особенностей (не только тип обхода менять надо, а например и тип самого объекта) количество наследников резко увеличивается.
Пример: 2 обхода — прямой, реверсивный и 2 типа ОС — виндоус, юникс дают уже 4 наследника. 3 обхода, 3 типа ОС — 9.

FDS>или

FDS>2. Достаточно передать в сам класс C делегат (указатель на функцию) сравнения элементов, как это делается в стандартной реализации qsort
FDS>То же можно задать и для фильтра.

FDS>Т.е., вообще говоря, тип обхода и фильтра можно изменить независимо от самого кода обхода, например, даже другим классом, если очень хочется


Достаточно. Это решение намного проще и единственно верное?
Можно и шаблоны использовать, но количество деталей, которые надо будет держать в голове при этом не уменьшится.
Re[12]: Дружественные классы
От: alzt  
Дата: 27.04.07 14:40
Оценка:
Здравствуйте, FDSC, Вы писали:

A>>Объект класса C предположим имеет скрытую переменную m_index.

A>>Тогда, если использовать один объект (ссылки, указатели на него), то разные потоки будут его изменять неопределённо.
A>>Я не смогу гарантировать, что m_index при обходе не будет изменён где-то ещё.

FDS>Кто мешает прямо в классе создавать разные контексты поиска для разных потоков?


На мой взгяд это будет сложнее. Можно также простейший пример реализации?
Re[10]: Дружественные классы
От: igna Россия  
Дата: 27.04.07 14:42
Оценка:

"ComeauTest.c", line 14: error: function returning abstract class "I" is not
          allowed:
            function "I::Reset" is a pure virtual function
            function "I::IsNext" is a pure virtual function
            function "I::Current" is a pure virtual function
      I GetIterator();
       ^


http://www.comeaucomputing.com/tryitout/
Re[11]: Дружественные классы
От: alzt  
Дата: 27.04.07 14:48
Оценка:
Здравствуйте, igna, Вы писали:

I>

I>

I>"ComeauTest.c", line 14: error: function returning abstract class "I" is not
I>          allowed:
I>            function "I::Reset" is a pure virtual function
I>            function "I::IsNext" is a pure virtual function
I>            function "I::Current" is a pure virtual function
I>      I GetIterator();
I>       ^
I>


I>http://www.comeaucomputing.com/tryitout/


А Вы чем компилируете? и почему "ComeauTest.c"?
VS2005 отлично компилирует, не думаю что в других компиляторах без специальных опций проблемы будут.
Re[11]: Дружественные классы
От: FDSC Россия consp11.github.io блог
Дата: 27.04.07 14:56
Оценка:
Здравствуйте, alzt, Вы писали:

FDS>>или

FDS>>2. Достаточно передать в сам класс C делегат (указатель на функцию) сравнения элементов, как это делается в стандартной реализации qsort
FDS>>То же можно задать и для фильтра.

FDS>>Т.е., вообще говоря, тип обхода и фильтра можно изменить независимо от самого кода обхода, например, даже другим классом, если очень хочется


A>Достаточно. Это решение намного проще и единственно верное?

A>Можно и шаблоны использовать, но количество деталей, которые надо будет держать в голове при этом не уменьшится.

Это решение проще с той точки зрения, что вместо нового класса итератора и новой функции в C мы будем писать только вспомогательную функцию. Так что это решение чем-то проще — не нужно изменять класс C
По крайней мере, оно и не сложней, тем более, что над C можно сделать такие же как и у вас обёртки I, которые уже ничего не будут знать о приватном интерфейсе C

Я попробую написать пример кода, но позже
Re[12]: Дружественные классы
От: igna Россия  
Дата: 27.04.07 15:03
Оценка:
Здравствуйте, alzt, Вы писали:

A>VS2005 отлично компилирует


Да, но вроде не должен, поскольку GetIterator пытается вернуть объект абстрактного класса I. Давай заменим на I* GetIterator()?
Re[13]: Дружественные классы
От: igna Россия  
Дата: 27.04.07 15:24
Оценка:
Если заменить возвращаемое значение на указатель, то можно то же самое написать без использования friend, если определить I и I1 внутри C:

#include <vector>

using namespace std;

class O
{
};

class I;

class C
{
    class I
    {
    public:
        virtual void Reset()=0;
        virtual bool IsNext()=0;
        virtual O Current()=0;
    protected:
        int Count(){return c->Count();}
        O GetElem(int index){return c->GetElem(index);}
    private:
        C* c;
    };

    class I1 : I
    {
    public:
        I1():m_index(0){}
        void Reset(){m_index = 0;}
        bool IsNext(){return m_index<I::Count();}
        O Current(){return I::GetElem(m_index);}
    private:
        int m_index;
    };
public:
    I* GetIterator();
private:
    vector<O> m_elems;
    int Count();
    O GetElem(int index);
};
Re[13]: Пример реализации
От: FDSC Россия consp11.github.io блог
Дата: 27.04.07 16:13
Оценка:
Здравствуйте, alzt, Вы писали:

A>Здравствуйте, FDSC, Вы писали:


A>>>Объект класса C предположим имеет скрытую переменную m_index.

A>>>Тогда, если использовать один объект (ссылки, указатели на него), то разные потоки будут его изменять неопределённо.
A>>>Я не смогу гарантировать, что m_index при обходе не будет изменён где-то ещё.

FDS>>Кто мешает прямо в классе создавать разные контексты поиска для разных потоков?


A>На мой взгяд это будет сложнее. Можно также простейший пример реализации?


Это на C# 2.0:
using System;
using System.Collections.Generic;
using System.Text;

namespace FriendDown
{
    public class C<T>
    {
        public class SearchCtx
        {
            private SearchFilter _SF;
            private SequenceFunc _CF;
            private int          _Number;

            public SearchCtx(SearchFilter SF, SequenceFunc CF)
            {
                ResetCounter();

                _SF        = SF;
                _CF        = CF;
            }

            private SearchCtx()
            {
                throw new System.NotSupportedException();
            }

            public void ResetCounter()
            {
                _Number = 0;
            }

            public int Number
            {
                get
                {
                    return _Number;
                }
            }

            public int ToNext
            {
                get 
                {
                    return _Number++;
                }
            }

            public SequenceFunc CF
            {
                get
                {
                    return _CF;
                }
            }

            public SearchFilter SF
            {
                get
                {
                    return _SF;
                }
            }
        }

        private List<T> _ObjectList = new List<T>();

        public delegate bool SearchFilter(T sender);
        public delegate int  SequenceFunc(int Number, int Count, object O);

        public bool Next(SearchCtx SC, object SearchParams, out T t)
        {
            bool isFound = false;
            do
            {
                int i = SC.CF(SC.ToNext, _ObjectList.Count, SearchParams);

                if (i >= _ObjectList.Count || i < 0)
                {
                    t = default(T);
                    return false;
                }

                t = _ObjectList[i];

                isFound = SC.SF(t);
            }
            while (!isFound);

            return true;
        }

        public void Add(T addT)
        {
            _ObjectList.Add(addT);
        }

        private object LockObj = new Object();

        public SearchCtx GetSearch(SearchFilter SF, SequenceFunc CF)
        {
                return new SearchCtx(SF, CF);
        }                
    }

    static class Filter2
    {
        public static bool SF(int sender)
        {
            return (sender & 1) == 0 ? true : false;
        }

        public static int CF(int Number, int Count, object O)
        {
            return Number;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            C<int> c = new C<int>();

            bool isEndInput = false;
            do
            {
                int i;
                isEndInput = !Int32.TryParse(Console.ReadLine(), out i);
                if (!isEndInput)
                  c.Add(i);
            }
            while (!isEndInput);

            C<int>.SearchCtx ctx1 = c.GetSearch(delegate(int sender) {return true;},
                                                delegate(int Number, int Count, object O) { return Count - Number - 1; }
                                                );

            C<int>.SearchCtx ctx2 = c.GetSearch(Filter2.SF, Filter2.CF);

            int val1, val2;

            while (c.Next(ctx1, null, out val1))
            {
                while (c.Next(ctx2, null, out val2))
                {
                    Console.WriteLine("ctx2: " + val2.ToString());
                }

                ctx2.ResetCounter();

                Console.WriteLine("ctx1: " + val1.ToString());
            }

            Console.ReadLine();
        }
    }
}
Re[14]: Дружественные классы
От: alzt  
Дата: 28.04.07 07:45
Оценка:
Здравствуйте, igna, Вы писали:

I>Если заменить возвращаемое значение на указатель, то можно то же самое написать без использования friend, если определить I и I1 внутри C:


Можно и так, сразу не пришло в голову.
Только тогда обернуть лучше указатель.
Re[13]: Дружественные классы
От: alzt  
Дата: 28.04.07 07:50
Оценка:
Здравствуйте, igna, Вы писали:

I>Здравствуйте, alzt, Вы писали:


A>>VS2005 отлично компилирует


I>Да, но вроде не должен, поскольку GetIterator пытается вернуть объект абстрактного класса I. Давай заменим на I* GetIterator()?


Понял почему он компилируется: GetIterator() я не определяю, а насчёт класса I — в момент определения класса C о нём ничего не известно (что он абстрактный). Надо указатель возвращать.

Проверить пока не могу — а в C# подобный пример скомпилируется?
Re[14]: Пример реализации
От: alzt  
Дата: 28.04.07 07:54
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Здравствуйте, alzt, Вы писали:


Выглядит не проще, не думаю, что в логике моего примера (корявого, как выяснилось выше — торопился) разобраться будет сложнее.
Re[15]: Пример реализации
От: FDSC Россия consp11.github.io блог
Дата: 28.04.07 08:48
Оценка:
Здравствуйте, alzt, Вы писали:

A>Здравствуйте, FDSC, Вы писали:


FDS>>Здравствуйте, alzt, Вы писали:


A>Выглядит не проще, не думаю, что в логике моего примера (корявого, как выяснилось выше — торопился) разобраться будет сложнее.


Выглядит не проще только потому что мой пример работает, а ваш — нет. И ещё мой пример обладает большей функциональностью, чем ваш
Уровень простоты примерно одинаков. Может быть у меня немного сложнее, но именно немного.

Зато не надо будет потом разбираться, что какой класс дружественный, какой не дружественный. Можно изменять класс C без всяких побочных эффектов на класс интерфейса.
Поэтому мой пример точно проще с точки зрения стратегической
Re[14]: Дружественные классы
От: FDSC Россия consp11.github.io блог
Дата: 28.04.07 08:49
Оценка:
Здравствуйте, alzt, Вы писали:

A>Здравствуйте, igna, Вы писали:


I>>Здравствуйте, alzt, Вы писали:


A>>>VS2005 отлично компилирует


I>>Да, но вроде не должен, поскольку GetIterator пытается вернуть объект абстрактного класса I. Давай заменим на I* GetIterator()?


A>Понял почему он компилируется: GetIterator() я не определяю, а насчёт класса I — в момент определения класса C о нём ничего не известно (что он абстрактный). Надо указатель возвращать.


A>Проверить пока не могу — а в C# подобный пример скомпилируется?



В C# нет дружественных классов. Это, кстати, тема топика
Re[15]: Дружественные классы
От: alzt  
Дата: 28.04.07 09:25
Оценка:
Здравствуйте, FDSC, Вы писали:

A>>Проверить пока не могу — а в C# подобный пример скомпилируется?


FDS>В C# нет дружественных классов. Это, кстати, тема топика


Я имел ввиду этот пример
Автор: igna
Дата: 27.04.07
.
Re[16]: Дружественные классы
От: FDSC Россия consp11.github.io блог
Дата: 28.04.07 10:39
Оценка:
Здравствуйте, alzt, Вы писали:

A>Здравствуйте, FDSC, Вы писали:


A>>>Проверить пока не могу — а в C# подобный пример скомпилируется?


FDS>>В C# нет дружественных классов. Это, кстати, тема топика


A>Я имел ввиду этот пример
Автор: igna
Дата: 27.04.07
.


Нечто подобное там возможно
Re[3]: Дружественные классы
От: vdimas Россия  
Дата: 30.04.07 08:55
Оценка:
Здравствуйте, Sinclair, Вы писали:

V>>В базовом фреймворке процент использования этой "концепции" зашкаливает за 50%, по моему субъективному наблюдению. Т.е. кол-во internal типов не уступает кол-ву публичных типов.

S>Гм. Не совсем так: интернальность классов никакого отношения к "дружбе" не имеет. Классы внутри сборки не имеют никакого особого доступа к членам интернал-классов.

Звучит как оксюморон. Именно тот самый "особый" доступ и имеют. С т.з. ограничения доступа public член internal типа вовсе не то же самое что public для public.

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


S>А интернал поля (которые были бы аналогом friend+private) применяются гораздо-гораздо реже.


Не только поля, полно еще методов и св-в internal в публичных классах фреймворка. А когда вижу internal члены у internal классов, то вообще ощущение как от неубранного мусора, но это всё уже частности, остающиеся на совести разработчиков фреймворка.
Re[4]: Дружественные классы
От: Sinclair Россия https://github.com/evilguest/
Дата: 02.05.07 02:38
Оценка:
Здравствуйте, vdimas, Вы писали:
V>Звучит как оксюморон. Именно тот самый "особый" доступ и имеют.
Ну нет же. В С++ friend имеет доступ ко всем приватным членам своего друга. Это вызывает зависимость от реализации, а не от публичного контракта. Интернальные классы устроены совершенно по-другому — они вообще скрыты от всех, кроме соседей по сборке. При этом эти соседи все равно могут пользоваться исключительно публичным интерфейсом.

V>Не только поля, полно еще методов и св-в internal в публичных классах фреймворка. А когда вижу internal члены у internal классов, то вообще ощущение как от неубранного мусора, но это всё уже частности, остающиеся на совести разработчиков фреймворка.

По-моему, это издержки декомпилятора. Если я не ошибаюсь, он все public мемберы интернал классов показывает как internal. А internal полей я что-то не припомню.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[5]: Дружественные классы
От: vdimas Россия  
Дата: 08.05.07 20:01
Оценка:
Здравствуйте, Sinclair, Вы писали:


S>Ну нет же. В С++ friend имеет доступ ко всем приватным членам своего друга. Это вызывает зависимость от реализации, а не от публичного контракта. Интернальные классы устроены совершенно по-другому — они вообще скрыты от всех, кроме соседей по сборке. При этом эти соседи все равно могут пользоваться исключительно публичным интерфейсом.


Во-первых, хорошим тоном в С++ считается, чтобы френды не лезли к полям, а лишь оперировали некоторыми приватными методами, что можно расценить по твоей терминологии как "публичный интерфейс внутри сборки". ИМХО, в С++ просто не стали раздувать иерархию доступа friend, обошлись минимально достаточным механизмом.

А во-вторых, что-то в последнее время всё сложнее беседовать в rsdn из-за предположения, будто собеседник чего-то там не знает.

Я акцентировался на задачах, т.е. вопрос стоит так: зачем вообще давать доступ к приватным членам в С++? Так вот, повторюсь, именно для этих самых задач internal подходит превосходно, тем более, что internal могут быть не только классы целиком, но и отдельные члены классов. В С++ получается предоставление доступа "наоборот" из-за самого способа распространения программ. Например, если мы распространяем бинарно, например, lib/dll-файлы и только h-заголовки к ним, то из всех этих h-заголовков перед распространением можно вообще убрать все объявления friend.

И еще, насчет зависимостей (маленький offtop) какие-то ограничение ВНУТРИ модуля — это уже даже несмешно, это клиника на мой взгляд. Я понимаю, когда речь идёт о монстрообразных базовых сборках, изготовляемыми десятками незнакомых программистов, но прикладные должны быть такого объема, дабы понятие "модуль" не потеряло свой компонентный смысл.

V>>Не только поля, полно еще методов и св-в internal в публичных классах фреймворка. А когда вижу internal члены у internal классов, то вообще ощущение как от неубранного мусора, но это всё уже частности, остающиеся на совести разработчиков фреймворка.

S>По-моему, это издержки декомпилятора. Если я не ошибаюсь, он все public мемберы интернал классов показывает как internal.

Нет, именно по-разному.

S>А internal полей я что-то не припомню.



System.Windows.Forms — гора
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.