1

Тема: Як правильно організувати дружню функцію??

#include "pch.h"
#include <iostream>
#include <Windows.h>
#include <math.h>

class Times
{
    friend double frd(Times, Times);

private:

    double lenght;
    double width;

public:

    Times() = default;
    Times(double _lenght, double _width) : lenght(_lenght), width(_width) {};

    void print_sqrt()
    {
        std::cout << "    Значення площи фігури = " << ((lenght * width) * 2) << '\n';
        std::cout << "    Розміри нового годинника : \n ";
        std::cout << "   Довжина = " << ((lenght + lenght) * 2) << '\n';
        std::cout << "    Ширина = " << ((width + width) * 2) << '\n';
        std::cout << "    Значення площи нового годинника = " << ((lenght * width) * 2) * 2 << '\n' << '\n';
    }

    void SetLenght(const double _lenght)
    {
        lenght = _lenght;
    }

    void SetWidth(const double _width)
    {
        width = _width;
    }
    
};

double frd(Times obj1, Times obj2)
{
    return  obj1.width + obj2,lenght ;
}

std::istream & operator >> (std::istream & ist, Times & ch)
{
    std::cout << "\t***********************\n\t** ПІСОЧНИЙ ГОДИННИК **\n\t***********************\n";
    double _lenght;
    double _width;
    std::cout << "    Введіть довжину = ";
    ist >> _lenght;
    std::cout << "    Введіть ширину = ";
    ist >> _width;
    ch.SetLenght(_lenght);
    ch.SetWidth(_width);
    return ist;
}

int main()
{
    SetConsoleCP(1251);
    SetConsoleOutputCP(1251);
    Times Times_two;
    std::cin >> Times_two;
    Times_two.print_sqrt();    
    Times obj1;
    Times obj2;
    std::cout << frd (obj1, obj2) <<"\n";
    system("pause");
}

2

Re: Як правильно організувати дружню функцію??

1. Яке взагалі завдання?
2. Чим вас не влаштовує ваш розв'язок?
Ну і взагалі - дружні функції порушують інкапсуляцію, і якщо є можливість, не треба їх використовувати.

3

Re: Як правильно організувати дружню функцію??

Потрібно використати дружню функцію, щоб додати дві сторони в класі, компілятор вибиває постійно помилки, або ієрогліфи

4

Re: Як правильно організувати дружню функцію??

Знаєте, у 70-80-х роках, коли розміри компіляторів обмежувалися, в кращому разі, десятками кілобайтів, казати "вибиває помилку" було нормально - бо компілятор просто повідомляв про помилку в коді, добре, якщо уточнював, в якому рядку. Але сучасні компілятори зазвичай вказують, де саме і в чому помилка, і часто навіть радять, як її виправити. Спробуйте прочитати і розібратися. Якщо не вийде - скопіюйте сюди повідомлення про першу помилку, розберемося.

буквоїдство

Якщо компілятор видає "ієрогліфи" (припускаю, що ви про неправильне кодування), причому не постійно, а іноді - швидше за все, він у вас погано налаштований. Спробуйте його перевстановити.

Утім, гадаю, це не компілятор видає то помилки, то ієрогліфи, а ви змінюєте код і, залежно від цього, отримуєте різні результати. Якщо ви все ще так робите - продовжуйте, доки код не запрацює, або більше не будете спроможні щось робити самостійно, і тоді звертайтеся на форум. Допомагати з кодом, який вже змінили і не будуть змінювати назад - гнилий номер.

5

Re: Як правильно організувати дружню функцію??

koala написав:

Ну і взагалі - дружні функції порушують інкапсуляцію, і якщо є можливість, не треба їх використовувати.

Ні. Дружні функції доповнюють інкапсуляцію. В C++ це спосіб надати функції доступ до приватних членів.
Іноді неможливо невикористовувати дружні функції.

6

Re: Як правильно організувати дружню функцію??

leofun01 написав:

надати функції доступ до приватних членів

Це і є порушення інкапсуляції.

7

Re: Як правильно організувати дружню функцію??

Для початку перевірте свій код на синтактичні помилки:

return  obj1.width + obj2,lenght ;

А потім вже наведіть помилки компілера (якщо вони взагалі будуть).

8

Re: Як правильно організувати дружню функцію??

https://www.urbandictionary.com/define.php?term=Lenght
Ось так нові слова і вивчаєш...

Подякували: leofun011

9

Re: Як правильно організувати дружню функцію??

Arete написав:
leofun01 написав:

надати функції доступ до приватних членів

Це і є порушення інкапсуляції.

Щось я не можу зрозуміти вашої логіки.
То по вашому [ get- | set- ] методи, які [ повертають | встановлюють ] значення приватних полів - це теж порушення інкапсуляції ?

Або можливо ви забули як функції робляться дружніми ? То я нагадаю :

#include <iostream>
using namespace std;

class SomeClass;

// Оголошення функції "println".
void println(SomeClass &, ostream &);

class SomeClass
{
    private:
        int _value;

    // Робимо функцію "println" дружньою для класу "SomeClass".
    friend void println(SomeClass &, ostream &);
};

// Реалізуємо функцію "println"
void println(SomeClass &obj, ostream &out)
{
    out << "Value : " << obj._value << endl;
    // Вивести приватне поле можна тільки коли
    // "SomeClass" містить оголошення, що
    // ця функція дружня для класу "SomeClass".
}

int main(int argc, char **argv)
{
    SomeClass obj;
    println(obj, cout);
    return 0;
}

На цьому прикладі видно, що для того, щоб зробити функцію println дружньою, автор класу SomeClass повинен явно це прописати (стрічка коду: 15). Без цього зовнішня функція не отримає доступ до приватних полів. Ну і де тут порушення інкапсуляції ? Поясніть мені.

10 Востаннє редагувалося koala (20.09.2018 06:22:20)

Re: Як правильно організувати дружню функцію??

Порушення інкапсуляції в тому, що методи класу є методами класу і для користувача класу є очевидним, що вони мають доступ до його даних; а от некоректна робота дружньої функції може бути неочікуваною.
Так, суто формально інкапсуляція не порушена; але по факту функція, від якої важко очікувати втручання в клас, отримує доступ до його даних напряму.

Подякували: leofun01, Arete, cheappi3863

11

Re: Як правильно організувати дружню функцію??

Інкапсуляцією я вважаю приховування приватних знань від якомога більшої кількості користувачів цих знань. Чим більше користувачів має доступ до цих знань тим більше порушена інкапсуляція. Тобто додавання дружньої функції до класу розширює список тих хто має доступ до приватних даних, а отже зменшує інкапсуляцію даних. Роботу з приватними даними інкапсулюють виставляючи назовні інтерфейс і цей інтерфейс спроектований і реалізований так щоб підтримувати інваріант даних. Тому я як користувач цього інтерфейсу розраховую на те що дані завжди знаходяться в інваріанті.

Схематичний приклад. Клас має свій узгоджений внутрішній стан до тих пір доки не буде використана дружня функція, яка може призвести до порушення інваріанту класа.

class Odd
{
public:
    void setA(int a) { a_ = a; isOdd_ = a_ % 2; }
    bool isOdd() const { return isOdd_; }
    friend void mess(Odd & t);
private:
    int a_;
    bool isOdd_;
};

void mess(Odd & t)
{
    t.a_ = 5;
}

В таких випадках користувачу класа потрібно не тільки розбиратись як користуватись інтерфейсом, але й ретельно досліджувати що робить дружня функція, оскільки впливає або може вплинути на внутрішній стан класу в обхід його інтерфейса. Фактично, функція вже є частиною інтерфейсу класа.
І я вважаю це порушенням інкапсуляції.

Подякували: koala, leofun012

12

Re: Як правильно організувати дружню функцію??

До речі, а й правда - у яких випадках реально неможливо зробити щось без дружньої функції? Адаптер з інлайном все зроблять так само:

class MyClass
{
   ...
   //замість friend ostream& operator<<(ostream&,MyClass&);
  void to_stream(ostream&);//пхаємо об'єкт у потік
}
inline ostream& operator<<(ostream& os,MyClass& obj)
{
    obj.to_stream(os);
    return os;
}

Все те саме, зате тепер видно, що щось робиться в класі, а не поза ним.

Подякували: leofun011

13

Re: Як правильно організувати дружню функцію??

Arete написав:

Інкапсуляцією я вважаю приховування приватних знань від якомога більшої кількості користувачів цих знань.

Ок. Тут норм.

Arete написав:

Чим більше користувачів має доступ до цих знань тим більше порушена інкапсуляція.

Допустимо.

Arete написав:

Тобто додавання дружньої функції до класу розширює список тих хто має доступ до приватних даних, а отже зменшує інкапсуляцію даних. Роботу з приватними даними інкапсулюють виставляючи назовні інтерфейс і цей інтерфейс спроектований і реалізований так щоб підтримувати інваріант даних. Тому я як користувач цього інтерфейсу розраховую на те що дані завжди знаходяться в інваріанті.

Ясне діло.

Arete написав:

Схематичний приклад. Клас має свій узгоджений внутрішній стан до тих пір доки не буде використана дружня функція, яка може призвести до порушення інваріанту класа.

Код
class Odd
{
public:
    void setA(int a) { a_ = a; isOdd_ = a_ % 2; }
    bool isOdd() const { return isOdd_; }
    friend void mess(Odd & t);
private:
    int a_;
    bool isOdd_;
};

void mess(Odd & t)
{
    t.a_ = 5;
}

Тут маю зауваження. Якщо автор класу (в даному випадку "Odd") робить функцію ("mess") дружньою, то автор цієї функції зобов'язаний зробити так, щоб внутрішній стан класу (або екземпляра) не був порушений на момент завершення роботи цієї функції.

Ясно, що ніхто не робить функцію дружньою, коли в цьому немає необхідності. Просто мені бомбануло від фрази: "дружні функції порушують інкапсуляцію".

Arete написав:

В таких випадках користувачу класа потрібно не тільки розбиратись як користуватись інтерфейсом, але й ретельно досліджувати що робить дружня функція, оскільки впливає або може вплинути на внутрішній стан класу в обхід його інтерфейса.

Не потрібно. Тут якраз автори класу і дружніх функцій повинні подбати про те, щоб внутрішній стан класу не став сюрпризом для користувача.

Arete написав:

Фактично, функція вже є частиною інтерфейсу класа.

Так.

Arete написав:

І я вважаю це порушенням інкапсуляції.

А я так не вважаю.

Подякували: Arete, koala2

14

Re: Як правильно організувати дружню функцію??

leofun01 написав:
Arete написав:

І я вважаю це порушенням інкапсуляції.

А я так не вважаю.

Це вже особисте відношення до ситуації, що тут можна сказати..  Поважаю вашу думку, вона не гірша і не краща за мою, просто інша :)


koala написав:

До речі, а й правда - у яких випадках реально неможливо зробити щось без дружньої функції?

Перше що приходить на думку - це приватний конструктор класу і фабрика-друг яка має ексклюзивне право створювати екземпляри класу.

Подякували: leofun011

15

Re: Як правильно організувати дружню функцію??

А хто заважає фабриці бути static public?

16

Re: Як правильно організувати дружню функцію??

koala написав:

https://www.urbandictionary.com/define.php?term=Lenght
Ось так нові слова і вивчаєш...

https://www.youtube.com/watch?v=hoLiKI5lzkA

koala написав:

До речі, а й правда - у яких випадках реально неможливо зробити щось без дружньої функції?

2 роки назад в мене був проект, там було кілька класів, і потрібно було приховати дані деякого класу від всіх інших класів крім одного, точніше крім його одного методу. Код того проекту я вже десь посіяв, але схематично це виглядало так :
Окремі файли -

// A.h

#ifndef A_H
#define A_H

#include "B.h"

class A {
    int _value;
    friend int B::Method();
};

#endif // A_H
// B.h

#ifndef B_H
#define B_H

class A;

class B {
    A *_a;

public:
    int Method();
};

#endif // B_H
// B.cpp

#include "B.h"
#include "A.h"

int B::Method() {
    int v = _a->_value;
    // купа всяких операцій.
    return v;
};

Там був такий випадок, що дружній метод був ідеальним рішенням.

17

Re: Як правильно організувати дружню функцію??

Нічого, але бувають такі випадки коли треба прив'язати створений фабрикою об'єкт до самої фабрики. Наприклад Http сесію cтворює тільки конкретний Http клієнт, або фабрика генерує ціле сімейство пов'язаних між собою об'єктів.

class HttpSession: public std::enable_shared_from_this<HttpSession>
{
public:
    typedef std::shared_ptr<HttpSession> PtrT;

// code
// ...
// code

private:
    HttpSession(const URL& host, uint16_t port);
    friend class HttpClient;
};

//factory for sessions
class HttpClient
{
public:

// code
// ...
// code

    HttpSession::PtrT openSession(const URL& host, uint16_t port = 0);
};
Подякували: leofun011