Friend
От: Europe_cn  
Дата: 08.08.13 06:20
Оценка:
На одном собеседовании поспорил с собеседующим что "друг" обычно первый признок плохого дизайна. На что мне долго пытались втирать, что это хорошо, а в некоторых случаях необходимо. Нопример оператор вывода в поток (С++).

Интересует ваше мнение. Друг зло или нет? Как часто вам приходилось им пользоваться?

Прошу не сносить в холивар, так как вопрос касается C++.
friend c++
Re: Friend
От: PM  
Дата: 08.08.13 06:56
Оценка:
E_>На одном собеседовании поспорил с собеседующим что "друг" обычно первый признок плохого дизайна. На что мне долго пытались втирать, что это хорошо, а в некоторых случаях необходимо. Нопример оператор вывода в поток (С++).

E_>Интересует ваше мнение. Друг зло или нет? Как часто вам приходилось им пользоваться?


Даже у типичного интроверта-программиста могут быть друзья

Мое мнение, что друзья класса — необходимость, иногда вынужденная (например чтобы разрешить создание объектов только из одного конкретного класса). Другое дело, что дружбой часто злоупотребляют там, где достаточно пары публичных функций. Еще одно мое скромное мнение, что вместо дружбы целыми классами лучше было бы иметь возможность объявлять другом функцию-член класса, что-то типа

friend void some_class::some_method();


Не знаю, есть ли такое предложение в C++11/14

Друзья ввода/вывод в поток в принципе не нужны, если есть такое:


class some_class
{
public:
   void print(std::ostream& os) const;
   void read(std::istream& is);
};

std::ostream& operator<<(std::ostream& os, some_class const& obj)
{
   obj.print(os);
   return os;
}

std::istream& operator>>(std::istream& os, some_class& obj)
{
   obj.read(is);
   return is;
}


С другой стороны, можно написать чуть короче

class some_class
{
public:
    friend std::ostream& operator<<(std::ostream& os, some_class const& obj)
    {
        // реализация print(os);
       return os;
    }

    friend std::istream& operator>>(std::istream& os, some_class& obj)
    {
        // реализация read(is);
        return is;
    }
};


Мне кажется это вопрос вкуса.

В общем, как обычно, у явления дружбы есть и преимущества, и недостатки.
Re: Friend
От: Tilir Россия http://tilir.livejournal.com
Дата: 08.08.13 07:10
Оценка: 4 (1)
Здравствуйте, Europe_cn, Вы писали:

E_>На одном собеседовании поспорил с собеседующим что "друг" обычно первый признок плохого дизайна. На что мне долго пытались втирать, что это хорошо, а в некоторых случаях необходимо. Нопример оператор вывода в поток (С++).

E_>Интересует ваше мнение. Друг зло или нет? Как часто вам приходилось им пользоваться?
E_>Прошу не сносить в холивар, так как вопрос касается C++.

1. Статическая дружба statefull классов вредна, т.к. нарушает инкапсуляцию.
2. Дружба класса с функцией сомнительна, но иногда упрощает разработку.
3. Но есть один сценарий, когда дружба крайне полезна. Дело в том, что в C++ друг статического родительского типа достаточен, чтобы делать дружественные вызовы полиморфных наследников. Звучит круто, да? =)

Синтетический пример:

class Parent
{
  friend class Family;
  protected:
    virtual void Answer() = 0;
};

class Child : public Parent
{
  private:
    void Answer() { printf("Child!\n"); }
    int encapsulated;
};

class Family
{
  public:
    void ParentAnswer(Parent *p) { p->Answer(); } /* ok */
    #if 0
      void ChildAnswer(Child *c) { c->Answer(); } /* error! */
      int breakEncapsulation(Child *c) { return c->encapsulated; } /* error again! */
    #endif
};

int test(void)
{
  Child c;
  Family f;
  f.ParentAnswer(&c); /* ok, Child->answer will be called */
  return 0;
}


Обратите внимание: Child статически не дружит с Family и его инкапсуляция не нарушена. Parent -- stateless там нечего нарушать. Но при этом, Family может вызывать закрытые методы класса Child если они полиморфны в его Parent.

Как мне кажется, ради подобных фишек дружба и оставлена в объектной модели C++. Ну ещё можно вспомнить Barton-Nackman trick, но он уже не актуален.
Re[2]: Friend
От: Lorenzo_LAMAS  
Дата: 08.08.13 07:33
Оценка: 6 (1)
PM>
PM>friend void some_class::some_method();
PM>


PM>Не знаю, есть ли такое предложение в C++11/14


да уж что тут не знать, это есть сто лет в обед, поди с первых версий C-фронта было, никаких "высоко-технологичных" C++11 и не надо.
Of course, the code must be complete enough to compile and link.
Re: Friend
От: Stanislav V. Zudin Россия  
Дата: 08.08.13 08:05
Оценка:
Здравствуйте, Europe_cn, Вы писали:

E_>На одном собеседовании поспорил с собеседующим что "друг" обычно первый признок плохого дизайна. На что мне долго пытались втирать, что это хорошо, а в некоторых случаях необходимо. Нопример оператор вывода в поток (С++).


E_>Интересует ваше мнение. Друг зло или нет? Как часто вам приходилось им пользоваться?


Пример: Контейнер и итераторы для обхода этого контейнера.
Вполне разумно итераторы сделать "друзьями", ибо все вместе они составляют единую систему.
_____________________
С уважением,
Stanislav V. Zudin
Re[3]: Friend
От: PM  
Дата: 08.08.13 08:06
Оценка:
Здравствуйте, Lorenzo_LAMAS, Вы писали:

PM>>
PM>>friend void some_class::some_method();
PM>>


PM>>Не знаю, есть ли такое предложение в C++11/14


L_L>да уж что тут не знать, это есть сто лет в обед, поди с первых версий C-фронта было, никаких "высоко-технологичных" C++11 и не надо.


Спасибо, действительно не знал Но не работает для еще не объявленных целиком классов, а именно такой случай мне реально требуется:

class A;

class B
{
    friend A::A(); // error C2027: use of undefined type 'A'
private:
    B() {}
};

class A
{
    A() {}
    B b;
};

A a;


И не поспоришь, действительно тип A еще не объявлен.
Re: Friend
От: igna Россия  
Дата: 08.08.13 08:43
Оценка: 6 (1) +4
Здравствуйте, Europe_cn, Вы писали:

E_>Интересует ваше мнение. Друг зло или нет? Как часто вам приходилось им пользоваться?


Использование friend нередко приводит к более естественному синтаксису:

class matrix {
    friend matrix mul(matrix const&, matrix const&);
    friend matrix inv(matrix const&);
    . . .
};

matrix a, b;

. . .

return mul(a, inv(b));


class matrix {
public:
    matrix mul(matrix const&) const;
    matrix inv() const;
    . . .
};

matrix a, b;

. . .

return a.mul(b.inv());


Возможно стоит всегда определять друга вместо члена, если не нужна виртуальность.
Re[2]: Friend
От: ioj Ниоткуда  
Дата: 08.08.13 11:46
Оценка: -2
Здравствуйте, igna, Вы писали:

I>Возможно стоит всегда определять друга вместо члена, если не нужна виртуальность.


нужно быть смелее, и переходить сразу к struct matrix с открытыми полями и набору ф-ций для работы с ней
нормально делай — нормально будет
Re[3]: Friend
От: igna Россия  
Дата: 08.08.13 11:49
Оценка:
Здравствуйте, ioj, Вы писали:

ioj>нужно быть смелее, и переходить сразу к struct matrix с открытыми полями и набору ф-ций для работы с ней


Зачем?
Re: Friend
От: artem.komisarenko Украина  
Дата: 08.08.13 12:11
Оценка: 24 (2) +4 :))) :)
Здравствуйте, Europe_cn, Вы писали:

E_>Прошу не сносить в холивар, так как вопрос касается C++


Да тут и холиварить-то не о чем. Если человек считает использование goto, friend, dynamic_cast, const_cast, reinterpret_cast, c-style cast, макросов нужное подчеркнуть плохим дизайном по определению, потому что прочитал об этом в каком-то там очередном блоге (со ссылкой в лучшем случае на Дейкстру, где тот говорил о Бейсике), значит он дурак по определению.
Re[3]: Friend
От: Evgeny.Panasyuk Россия  
Дата: 08.08.13 12:54
Оценка:
Здравствуйте, ioj, Вы писали:

I>>Возможно стоит всегда определять друга вместо члена, если не нужна виртуальность.

ioj>нужно быть смелее, и переходить сразу к struct matrix с открытыми полями и набору ф-ций для работы с ней

В class matrix обычно vector<Scalar> storage; + информация о размерностях, которая вместе с размером storage составляют инвариант, который нельзя рушить.
Re[2]: Friend
От: Evgeny.Panasyuk Россия  
Дата: 08.08.13 13:13
Оценка:
Здравствуйте, artem.komisarenko, Вы писали:

AK>Да тут и холиварить-то не о чем. Если человек считает использование goto, friend, dynamic_cast, const_cast, reinterpret_cast, c-style cast, макросов нужное подчеркнуть плохим дизайном по определению, потому что прочитал об этом в каком-то там очередном блоге (со ссылкой в лучшем случае на Дейкстру, где тот говорил о Бейсике), значит он дурак по определению.


+1
Некоторые даже пытаются через свою фанатичную веру в то что какая-то конструкция является плохой по определению, доказать что другие конструкции которые сводятся к этим, которые "плохие по определению", тоже являются плохими.

Недавно на одном из форумов так в очередной раз пытались "доказать" вредность исключений — типа это аналог goto, соответственно исключения — это "плохо":

> throw is a way to put GOTO in your code, the more you see throw or catch , the more your code is full of flaw.

и соответственно мой ответ:

By using your "GOTO" reasoning we can go even further — we can consider harmful even "if" statements. Because at lowest level "if" statement translates into something like:

if(condition) goto label; // jne, brne, etc

So, by using your conclusions: "the more you see if statement, the more your code is full of flaw" — how funny is that?

Moreover, the greatest of us actually claim that there is no syntactic limitations in semantic questions, and goto is useful. Prohibiting it fanatically for all cases is not a good idea.
Alexander Stepanov shows how goto statement can be useful:
http://www.youtube.com/watch?v=mrtaApqAjA8&amp;t=5m38s
http://books.google.com/books?id=CO9ULZGINlsC&amp;lpg=PT209&amp;ots=Ow3EbqSBqA&amp;dq=elements+of+programming+split_linked&amp;hl=en&amp;pg=PT210

Re[4]: Friend
От: ioj Ниоткуда  
Дата: 08.08.13 14:35
Оценка: :)
Здравствуйте, igna, Вы писали:

I>Зачем?


чтобы не плодить сущности сверх необходимого
нормально делай — нормально будет
Re[4]: Friend
От: ioj Ниоткуда  
Дата: 08.08.13 14:40
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:


EP>В class matrix обычно vector<Scalar> storage; + информация о размерностях, которая вместе с размером storage составляют инвариант, который нельзя рушить.


я не хочу скатываться в холивар а-ля anemic vs rich, просто по мне выбор класс + френды для операций вместо свободных ф-ций и простой структуры выглядит несколько странным.
нормально делай — нормально будет
Re[5]: Friend
От: Evgeny.Panasyuk Россия  
Дата: 08.08.13 14:50
Оценка: +1
Здравствуйте, ioj, Вы писали:

EP>>В class matrix обычно vector<Scalar> storage; + информация о размерностях, которая вместе с размером storage составляют инвариант, который нельзя рушить.

ioj>я не хочу скатываться в холивар а-ля anemic vs rich, просто по мне выбор класс + френды для операций вместо свободных ф-ций и простой структуры выглядит несколько странным.

Почему? friend'ы класса указываются в нём самом — они точно такие же члены его интерфейса (которые имеют доступ к инвариантам) как и member functions — основное отличие в синтаксисе использования.
Причём синтаксис non-member более общий чем member.
Например можно сделать функцию inverse и для double и для matrix:
double x = ...;
Matrix y = .../
auto z = inverse(value);
auto solution = inverse(y) * b;


Посмотри например Notes on Programming Степанова:

While we could make a member function to return length, it is better to make it a global friend function. If we do that, we will be able eventually to define the same function to work on built-in arrays and achieve greater uniformity of design. I made size into a member function in STL in an attempt to please the standard committee. I knew that begin, end and size should be global functions but was not willing to risk another fight with the committee. In general, there were many compromises of which I am ashamed. It would have been harder to succeed without making them, but I still get a metallic taste in my mouth when I encounter all the things that I did wrong while knowing full how to do them right.

Там class fvector_int, у которого данные private, и есть
friend std::size_t size(const fvector_int& x)


То что мы даём доступ к инвариантам класса некоторым функциям через friend — не означает, что мы даём его всем.
Re[2]: Friend
От: Europe_cn  
Дата: 08.08.13 15:04
Оценка: -1
Здравствуйте, artem.komisarenko, Вы писали:

Сечёшь разницу между "первый признок плохого дизайна" и плохим дизайном по определению есть?

Все конструкции были добавлены в язык по объективным причинам, но не все конструкции нужно использовать при первом удобном случае.
Re[3]: Friend
От: Evgeny.Panasyuk Россия  
Дата: 08.08.13 15:15
Оценка:
Здравствуйте, Europe_cn, Вы писали:

E_>Здравствуйте, artem.komisarenko, Вы писали:

E_>Сечёшь разницу между "первый признок плохого дизайна" и плохим дизайном по определению есть?

Лучше расскажи нам про разницу между "первый признок плохого дизайна" и "Друг зло или нет?"

E_>Интересует ваше мнение. Друг зло или нет? Как часто вам приходилось им пользоваться?

Re[3]: Friend
От: Stanislav V. Zudin Россия  
Дата: 08.08.13 15:15
Оценка:
Здравствуйте, Europe_cn, Вы писали:

E_>Все конструкции были добавлены в язык по объективным причинам, но не все конструкции нужно использовать при первом удобном случае.


А при втором удобном случае можно?
Где граница?
_____________________
С уважением,
Stanislav V. Zudin
Re[5]: Friend
От: igna Россия  
Дата: 08.08.13 15:22
Оценка: +1
Здравствуйте, ioj, Вы писали:

ioj>чтобы не плодить сущности сверх необходимого


Так и проверку типов можно отменить. Чтобы не плодить.
Re[4]: Friend
От: Europe_cn  
Дата: 08.08.13 15:31
Оценка:
Здравствуйте, Stanislav V. Zudin, Вы писали:

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


E_>>Все конструкции были добавлены в язык по объективным причинам, но не все конструкции нужно использовать при первом удобном случае.


SVZ>А при втором удобном случае можно?

SVZ>Где граница?

Границы нет по определению. Главное чтобы аргументация была. Одну и ту же фичу можно реализовать по разному. И стоимость поддержки будет тоже разная.
Весь код тоже можно в main запихнуть.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.