Здравствуйте, nikov, Вы писали:
N>Дружественные (friend) классы — полезная ли это концепция? И почему в .NET от нее отказались?
Она только запутывает архитектуру приложения, делает большей частных связей между классами, усложняя её понимание. С другой стороны, без дружественных классов можно обойтись, как и без множественного наследования.
Вообще, говорят, использование дружественных классов является признаком плохой архитектуры приложения
Здравствуйте, FDSC, Вы писали:
FDS>Она только запутывает архитектуру приложения, делает большей частных связей между классами, усложняя её понимание. С другой стороны, без дружественных классов можно обойтись, как и без множественного наследования.
Точно. И вообще без наследования, кстати, можно обойтись. Отличные есть языки программирования без всего этого — С, например.
(не, я понимаю, что наследование всё-таки полезнее дружбы, просто, имхо, подобные аргументы критики не выдерживают)
FDS>Вообще, говорят, использование дружественных классов является признаком плохой архитектуры приложения
Врут. Не верьте им.
Признаком плохой архитектуры является использование дружественных классов неправильно. То же можно сказать про множественное наследование, обычное наследование, функции, макросы и т.п.
Честно говоря, сам очень редко использую. На дружественных классах/функциях делаются фабрики классов, делаются операторы-не-члены-коласса.
Хотя, есть еще "методическая" сторона. Если в 99% процентах случаев на практике возможность используется именно неправильно, возможно, стоит выкинуть её из языка. Но тогда я начал бы всё же с goto
В C# они эмулируются при помощи модификатора internal и вынесения классов в отдельную сборку. А вообще, исключение дружественных классов — это дурь проектировщиков шарпа, ИМХО.
Здравствуйте, AndreiF, Вы писали:
AF>Здравствуйте, SergH, Вы писали:
AF>В C# они эмулируются при помощи модификатора internal и вынесения классов в отдельную сборку. А вообще, исключение дружественных классов — это дурь проектировщиков шарпа, ИМХО.
Ну, я бы сказал, у меня, когда я начинал изучать C# ощущение было очень простое: "всё, что есть в Delphi — есть в C#, остальное решили не делать"
Здравствуйте, Igor Trofimov, Вы писали:
FDS>>Ну, я бы сказал, у меня, когда я начинал изучать C# ощущение было очень простое: "всё, что есть в Delphi — есть в C#, остальное решили не делать"
iT>Ну, во-первых не все, а во-вторых, не только это
Ну, я же говорю про впечатление. Конечно не только. Мне это впечатление очень мешало первое время: различий-то там много
N>Дружественные (friend) классы — полезная ли это концепция? И почему в .NET от нее отказались?
С. Макконнелл, Совершенный код. 2-е издание
Избегайте использования дружественных классов
Иногда — например, при реализации шаблона Состояние (State) — дисциплинированное использование дружественных классов помогает управлять сложностью (Gamma et al., 1995).
Однако обычно дружественные классы нарушают инкапсуляцию. Они увеличивают объем кода, о котором приходится думать в каждый конкретный момент времени, повышая тем самым сложность программы.
Полностью солидарен с автором книги в этом вопросе.
Здравствуйте, sharcUs, Вы писали:
U>С. Макконнелл, Совершенный код. 2-е издание
U>
U>Избегайте использования дружественных классов
U>Иногда — например, при реализации шаблона Состояние (State) — дисциплинированное использование дружественных классов помогает управлять сложностью (Gamma et al., 1995).
U>Однако обычно дружественные классы нарушают инкапсуляцию. Они увеличивают объем кода, о котором приходится думать в каждый конкретный момент времени, повышая тем самым сложность программы.
U>Полностью солидарен с автором книги в этом вопросе.
Если не использовать френдов, то вместо того чтобы показать внутренности одному классу, придется так или иначе показать их всем. В результате объем кода, "о котором приходится думать в каждый конкретный момент времени" возрастает на порядки. Т.о. френды как раз таки способствуют инкапсуляции.
Здравствуйте, Zuka, Вы писали:
Z>Здравствуйте, sharcUs, Вы писали:
Z>Если не использовать френдов, то вместо того чтобы показать внутренности одному классу, придется так или иначе показать их всем. В результате объем кода, "о котором приходится думать в каждый конкретный момент времени" возрастает на порядки. Т.о. френды как раз таки способствуют инкапсуляции.
В БОЛЬШИНСТВЕ случаев необходимость использования друзей свидетельствует о неправильном архитектурном решении, приводящем к усложнению объектной модели проекта и его дальнейшем сопровождении. К примеру, у меня НИ РАЗУ не появилась потребность в их использовании, хотя в прошлом уже не один завершенный проект, как на С++ так и на С#. Как правило в 98% процентах случаев включение и наследование вполне достаточно для решения требуемых задач.
Вместе с тем я не отрицаю, что в исключительных случаях использование друзей является ЕДИНСТВЕННО ВОЗМОЖНЫМ решением, однако ДЕЙСТВИТЕЛЬНАЯ необходимость их использования ограничивается единичными случаями, как и говорит Стив в приведенной мной цитате выше. Как правило потребность в дружественных сущностях является ОШИБКОЙ в архитектуре программной модели, от которой следует избавляться и использовать вместо нее другие подходы.
Здравствуйте, sharcUs, Вы писали:
U>В БОЛЬШИНСТВЕ случаев необходимость использования друзей свидетельствует о неправильном архитектурном решении, приводящем к усложнению объектной модели проекта и его дальнейшем сопровождении. К примеру, у меня НИ РАЗУ не появилась потребность в их использовании, хотя в прошлом уже не один завершенный проект, как на С++ так и на С#. Как правило в 98% процентах случаев включение и наследование вполне достаточно для решения требуемых задач.
Есть ещё другой вариант — использовать интерфейсы, выдаваемые "по требованию". Скажем, есть параметр, и всем только читать его могут и лишь "избранные" писать, делаем отдельный интерфейс "сеттерный" и функцию получения его. Связность получается более прослеживаемая, и добавляется большая расширяемость (т.к. для новых "сеттеров" не надо будет править исходный класс, добавляя в него френдов)
Здравствуйте, nikov, Вы писали:
N>Дружественные (friend) классы — полезная ли это концепция? И почему в .NET от нее отказались?
На мой взгляд — полезная. А в дотнете очень могого нет по сугубо личностным соображениям его создателей и по причинам неудобности реализации (ну, типа это потребует много сил, так что ну его на).
Другое дело, что вообще, на мой взгляд проблемы инкапсуляции несколько преувеличены. Да и большинство проблем с инкапсуляцией появляются вовсе не из-за ограничений языка/среды, а в следствии невнимательности разработчиков.
ЗЫ
Кстати, умнее было бы сделать не дружественные классы, а дружественные интерфесы или даже дружественный список методов. Ведь для взаимодействия свяазнных типов порой достаточно одного-двух методов, а классу открываются все потроха своего друга, что только усложняет поддержку кода.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, 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 нельзя.
Здравствуйте, nikov, Вы писали:
N>Дружественные (friend) классы — полезная ли это концепция? И почему в .NET от нее отказались?
Не совсем отказались, вот навскидку пара аналогов:
1. В .Net есть способ ограничение видимости в пределах сборки, (internal в C#), соответственно internal класс, или internal члены публичных классов являются дружественными всем классам сборки.
2. Вложенные классы по-умолчанию являются дружественными объемлющему классу.
В базовом фреймворке процент использования этой "концепции" зашкаливает за 50%, по моему субъективному наблюдению. Т.е. кол-во internal типов не уступает кол-ву публичных типов.
Здравствуйте, vdimas, Вы писали: V>В базовом фреймворке процент использования этой "концепции" зашкаливает за 50%, по моему субъективному наблюдению. Т.е. кол-во internal типов не уступает кол-ву публичных типов.
Гм. Не совсем так: интернальность классов никакого отношения к "дружбе" не имеет. Классы внутри сборки не имеют никакого особого доступа к членам интернал-классов.
А интернал поля (которые были бы аналогом friend+private) применяются гораздо-гораздо реже.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, 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, и немного его наследники.
Остальные знают только об интерфейсе итератора.
Здравствуйте, alzt, Вы писали:
A>В итоге о внутренней структуре знает только класс I, и немного его наследники. A>Остальные знают только об интерфейсе итератора.
А в чём преимущество этой архитектуры по сравнению с реализацией итератора прямо в C и его наследниках?
Т.е. вопрос, зачем вы разделяете класс I и класс C? Почему не перенести функциональность класса I в класс C?
Здравствуйте, FDSC, Вы писали:
FDS>Т.е. вопрос, зачем вы разделяете класс I и класс C? Почему не перенести функциональность класса I в класс C?
+ сделать публичный интерфейс, а доступ к внутреннему классу I закрыть (а то так можно создать экземпляр итератора без коллекции, это обходится, но зачем добавлять костыли?).
Здравствуйте, FDSC, Вы писали:
FDS>Здравствуйте, alzt, Вы писали:
A>>В итоге о внутренней структуре знает только класс I, и немного его наследники. A>>Остальные знают только об интерфейсе итератора.
FDS>А в чём преимущество этой архитектуры по сравнению с реализацией итератора прямо в C и его наследниках? FDS>Т.е. вопрос, зачем вы разделяете класс I и класс C? Почему не перенести функциональность класса I в класс C?
Такая ситуация: один экземпляр класса C. Необходимо обеспечить его обход в двух потоках.
Если использовать один класс C, то придётся его копировать, что не всегда хорошо.
Здравствуйте, alzt, Вы писали:
A>Здравствуйте, FDSC, Вы писали:
FDS>>Здравствуйте, alzt, Вы писали:
A>>>В итоге о внутренней структуре знает только класс I, и немного его наследники. A>>>Остальные знают только об интерфейсе итератора.
FDS>>А в чём преимущество этой архитектуры по сравнению с реализацией итератора прямо в C и его наследниках? FDS>>Т.е. вопрос, зачем вы разделяете класс I и класс C? Почему не перенести функциональность класса I в класс C?
A>Такая ситуация: один экземпляр класса C. Необходимо обеспечить его обход в двух потоках. A>Если использовать один класс C, то придётся его копировать, что не всегда хорошо.
Здравствуйте, 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;
};
Здравствуйте, FDSC, Вы писали:
A>>Такая ситуация: один экземпляр класса C. Необходимо обеспечить его обход в двух потоках. A>>Если использовать один класс C, то придётся его копировать, что не всегда хорошо.
FDS>С какой стати его придётся копировать?
Объект класса C предположим имеет скрытую переменную m_index.
Тогда, если использовать один объект (ссылки, указатели на него), то разные потоки будут его изменять неопределённо.
Я не смогу гарантировать, что m_index при обходе не будет изменён где-то ещё.
Здравствуйте, alzt, Вы писали:
A>Здравствуйте, FDSC, Вы писали:
A>>>Такая ситуация: один экземпляр класса C. Необходимо обеспечить его обход в двух потоках. A>>>Если использовать один класс C, то придётся его копировать, что не всегда хорошо.
FDS>>С какой стати его придётся копировать?
A>Объект класса C предположим имеет скрытую переменную m_index. A>Тогда, если использовать один объект (ссылки, указатели на него), то разные потоки будут его изменять неопределённо. A>Я не смогу гарантировать, что m_index при обходе не будет изменён где-то ещё.
Кто мешает прямо в классе создавать разные контексты поиска для разных потоков?
Здравствуйте, FDSC, Вы писали:
A>>В итоге о внутренней структуре знает только класс I, и немного его наследники. A>>Остальные знают только об интерфейсе итератора.
FDS>А в чём преимущество этой архитектуры по сравнению с реализацией итератора прямо в C и его наследниках? FDS>Т.е. вопрос, зачем вы разделяете класс I и класс C? Почему не перенести функциональность класса I в класс C?
А если мне потребуется ещё обход I2, скажем такой, чтобы объекты типа O перебирались по алфавиту, или только такие, которые имеют какое-то свойство, а остальные пропускались.
Тогда вся логика обхода будет сосредоточена в одном классе, а от класса C мне потребуется только вернуть нужный вид обхода.
Если же перенести функциональность класса I в класс C, то менять будет сложнее.
Здравствуйте, 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
То же можно задать и для фильтра.
Т.е., вообще говоря, тип обхода и фильтра можно изменить независимо от самого кода обхода, например, даже другим классом, если очень хочется
Здравствуйте, 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>Т.е., вообще говоря, тип обхода и фильтра можно изменить независимо от самого кода обхода, например, даже другим классом, если очень хочется
Достаточно. Это решение намного проще и единственно верное?
Можно и шаблоны использовать, но количество деталей, которые надо будет держать в голове при этом не уменьшится.
Здравствуйте, FDSC, Вы писали:
A>>Объект класса C предположим имеет скрытую переменную m_index. A>>Тогда, если использовать один объект (ссылки, указатели на него), то разные потоки будут его изменять неопределённо. A>>Я не смогу гарантировать, что m_index при обходе не будет изменён где-то ещё.
FDS>Кто мешает прямо в классе создавать разные контексты поиска для разных потоков?
На мой взгяд это будет сложнее. Можно также простейший пример реализации?
"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();
^
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>
Здравствуйте, alzt, Вы писали:
FDS>>или FDS>>2. Достаточно передать в сам класс C делегат (указатель на функцию) сравнения элементов, как это делается в стандартной реализации qsort FDS>>То же можно задать и для фильтра.
FDS>>Т.е., вообще говоря, тип обхода и фильтра можно изменить независимо от самого кода обхода, например, даже другим классом, если очень хочется
A>Достаточно. Это решение намного проще и единственно верное? A>Можно и шаблоны использовать, но количество деталей, которые надо будет держать в голове при этом не уменьшится.
Это решение проще с той точки зрения, что вместо нового класса итератора и новой функции в C мы будем писать только вспомогательную функцию. Так что это решение чем-то проще — не нужно изменять класс C
По крайней мере, оно и не сложней, тем более, что над C можно сделать такие же как и у вас обёртки I, которые уже ничего не будут знать о приватном интерфейсе C
Если заменить возвращаемое значение на указатель, то можно то же самое написать без использования 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);
};
Здравствуйте, 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();
}
}
}
Здравствуйте, igna, Вы писали:
I>Если заменить возвращаемое значение на указатель, то можно то же самое написать без использования friend, если определить I и I1 внутри C:
Можно и так, сразу не пришло в голову.
Только тогда обернуть лучше указатель.
Здравствуйте, igna, Вы писали:
I>Здравствуйте, alzt, Вы писали:
A>>VS2005 отлично компилирует
I>Да, но вроде не должен, поскольку GetIterator пытается вернуть объект абстрактного класса I. Давай заменим на I* GetIterator()?
Понял почему он компилируется: GetIterator() я не определяю, а насчёт класса I — в момент определения класса C о нём ничего не известно (что он абстрактный). Надо указатель возвращать.
Проверить пока не могу — а в C# подобный пример скомпилируется?
Здравствуйте, alzt, Вы писали:
A>Здравствуйте, FDSC, Вы писали:
FDS>>Здравствуйте, alzt, Вы писали:
A>Выглядит не проще, не думаю, что в логике моего примера (корявого, как выяснилось выше — торопился) разобраться будет сложнее.
Выглядит не проще только потому что мой пример работает, а ваш — нет. И ещё мой пример обладает большей функциональностью, чем ваш
Уровень простоты примерно одинаков. Может быть у меня немного сложнее, но именно немного.
Зато не надо будет потом разбираться, что какой класс дружественный, какой не дружественный. Можно изменять класс C без всяких побочных эффектов на класс интерфейса.
Поэтому мой пример точно проще с точки зрения стратегической
Здравствуйте, alzt, Вы писали:
A>Здравствуйте, igna, Вы писали:
I>>Здравствуйте, alzt, Вы писали:
A>>>VS2005 отлично компилирует
I>>Да, но вроде не должен, поскольку GetIterator пытается вернуть объект абстрактного класса I. Давай заменим на I* GetIterator()?
A>Понял почему он компилируется: GetIterator() я не определяю, а насчёт класса I — в момент определения класса C о нём ничего не известно (что он абстрактный). Надо указатель возвращать.
A>Проверить пока не могу — а в C# подобный пример скомпилируется?
В C# нет дружественных классов. Это, кстати, тема топика
Здравствуйте, FDSC, Вы писали:
A>>Проверить пока не могу — а в C# подобный пример скомпилируется?
FDS>В C# нет дружественных классов. Это, кстати, тема топика
Здравствуйте, alzt, Вы писали:
A>Здравствуйте, FDSC, Вы писали:
A>>>Проверить пока не могу — а в C# подобный пример скомпилируется?
FDS>>В C# нет дружественных классов. Это, кстати, тема топика
A>Я имел ввиду этот пример
Здравствуйте, Sinclair, Вы писали:
V>>В базовом фреймворке процент использования этой "концепции" зашкаливает за 50%, по моему субъективному наблюдению. Т.е. кол-во internal типов не уступает кол-ву публичных типов. S>Гм. Не совсем так: интернальность классов никакого отношения к "дружбе" не имеет. Классы внутри сборки не имеют никакого особого доступа к членам интернал-классов.
Звучит как оксюморон. Именно тот самый "особый" доступ и имеют. С т.з. ограничения доступа public член internal типа вовсе не то же самое что public для public.
Про friend я и не утверждал, что это совсем одно и то же, было сказано про аналоги, которые позволяют решать те же задачи, для чего придуман friend.
S>А интернал поля (которые были бы аналогом friend+private) применяются гораздо-гораздо реже.
Не только поля, полно еще методов и св-в internal в публичных классах фреймворка. А когда вижу internal члены у internal классов, то вообще ощущение как от неубранного мусора, но это всё уже частности, остающиеся на совести разработчиков фреймворка.
Здравствуйте, vdimas, Вы писали: V>Звучит как оксюморон. Именно тот самый "особый" доступ и имеют.
Ну нет же. В С++ friend имеет доступ ко всем приватным членам своего друга. Это вызывает зависимость от реализации, а не от публичного контракта. Интернальные классы устроены совершенно по-другому — они вообще скрыты от всех, кроме соседей по сборке. При этом эти соседи все равно могут пользоваться исключительно публичным интерфейсом.
V>Не только поля, полно еще методов и св-в internal в публичных классах фреймворка. А когда вижу internal члены у internal классов, то вообще ощущение как от неубранного мусора, но это всё уже частности, остающиеся на совести разработчиков фреймворка.
По-моему, это издержки декомпилятора. Если я не ошибаюсь, он все public мемберы интернал классов показывает как internal. А internal полей я что-то не припомню.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
S>Ну нет же. В С++ friend имеет доступ ко всем приватным членам своего друга. Это вызывает зависимость от реализации, а не от публичного контракта. Интернальные классы устроены совершенно по-другому — они вообще скрыты от всех, кроме соседей по сборке. При этом эти соседи все равно могут пользоваться исключительно публичным интерфейсом.
Во-первых, хорошим тоном в С++ считается, чтобы френды не лезли к полям, а лишь оперировали некоторыми приватными методами, что можно расценить по твоей терминологии как "публичный интерфейс внутри сборки". ИМХО, в С++ просто не стали раздувать иерархию доступа friend, обошлись минимально достаточным механизмом.
А во-вторых, что-то в последнее время всё сложнее беседовать в rsdn из-за предположения, будто собеседник чего-то там не знает.
Я акцентировался на задачах, т.е. вопрос стоит так: зачем вообще давать доступ к приватным членам в С++? Так вот, повторюсь, именно для этих самых задач internal подходит превосходно, тем более, что internal могут быть не только классы целиком, но и отдельные члены классов. В С++ получается предоставление доступа "наоборот" из-за самого способа распространения программ. Например, если мы распространяем бинарно, например, lib/dll-файлы и только h-заголовки к ним, то из всех этих h-заголовков перед распространением можно вообще убрать все объявления friend.
И еще, насчет зависимостей (маленький offtop) какие-то ограничение ВНУТРИ модуля — это уже даже несмешно, это клиника на мой взгляд. Я понимаю, когда речь идёт о монстрообразных базовых сборках, изготовляемыми десятками незнакомых программистов, но прикладные должны быть такого объема, дабы понятие "модуль" не потеряло свой компонентный смысл.
V>>Не только поля, полно еще методов и св-в internal в публичных классах фреймворка. А когда вижу internal члены у internal классов, то вообще ощущение как от неубранного мусора, но это всё уже частности, остающиеся на совести разработчиков фреймворка. S>По-моему, это издержки декомпилятора. Если я не ошибаюсь, он все public мемберы интернал классов показывает как internal.
Нет, именно по-разному.
S>А internal полей я что-то не припомню.