12.15 Friend functions and classes

주홍영·2022년 3월 17일
0

Learncpp.com

목록 보기
139/199

https://www.learncpp.com/cpp-tutorial/friend-functions-and-classes/

Friend functions

friend function 이란 일반 함수인데 member의 private value에 접근할 수 있도록 허용해주는 키워드 이다
다음의 예시를 보자

class Accumulator
{
private:
    int m_value { 0 };

public:
    void add(int value) { m_value += value; }

    // Make the reset() function a friend of this class
    friend void reset(Accumulator& accumulator);
};

// reset() is now a friend of the Accumulator class
void reset(Accumulator& accumulator)
{
    // And can access the private data of Accumulator objects
    accumulator.m_value = 0;
}

int main()
{
    Accumulator acc;
    acc.add(5); // add 5 to the accumulator
    reset(acc); // reset the accumulator to 0

    return 0;
}

void reset이라는 function이 accumulator라는 Accumulator 클래스의 변수를 parameter로 받고 있다
원래대로라면 reset은 accumulator의 private variable에 접근할 수 없어야 하지만
friend라는 키워드로 접근을 허용해주었기 때문에 m_value를 수정할 수 있는 것이다

우리는 클래스의 변수에 접근하고 싶은 클래스에서 friend 로 declare 해주면 된다

참고로 파라미터로 전달을 해줘야지 멤버 변수에 접근할 수 있다는 것은 매우 합리적인 생각이다
만약 파라미터로 어떤 객체를 전달 받는지도 모른체로는 member variable에 접근할 수 없을 것이다

여기 another example이 있다

#include <iostream>

class Value
{
private:
    int m_value{};

public:
    Value(int value)
        : m_value{ value }
    {
    }

    friend bool isEqual(const Value& value1, const Value& value2);
};

bool isEqual(const Value& value1, const Value& value2)
{
    return (value1.m_value == value2.m_value);
}

int main()
{
    Value v1{ 5 };
    Value v2{ 6 };
    std::cout << std::boolalpha << isEqual(v1, v2);

    return 0;
}

위와 같이 복수의 동일 클래스를 받아서 사용하는 것도 가능하다

Multiple friends

함수는 동일 타입의 클래스 뿐만이 아닌 복수의 클래스에서 동시에 friend일 수도 있다

#include <iostream>

class Humidity;

class Temperature
{
private:
    int m_temp {};

public:
    Temperature(int temp=0)
        : m_temp { temp }
    {
    }

    friend void printWeather(const Temperature& temperature, const Humidity& humidity);
};

class Humidity
{
private:
    int m_humidity {};

public:
    Humidity(int humidity=0)
        : m_humidity { humidity }
    {
    }

    friend void printWeather(const Temperature& temperature, const Humidity& humidity);
};

void printWeather(const Temperature& temperature, const Humidity& humidity)
{
    std::cout << "The temperature is " << temperature.m_temp <<
       " and the humidity is " << humidity.m_humidity << '\n';
}

int main()
{
    Humidity hum(10);
    Temperature temp(12);

    printWeather(temp, hum);

    return 0;
}

위 코드에서 주목할 점은 both class에서 frien라고 선언되어야 하고
양쪽 클래스를 언급해야 하므로 forward declaration으로 Humidity 클래스를
forward decl하고 있다

또한 클래스는 return type이 존재하지 않기에 간단히 class ClassName;으로 선언하는 것을 알 수 있다

Friend classes

추가적으로 클래스 전체를 다른 클래스의 friend로 선언하는 것도 가능하다
exmaple:

#include <iostream>

class Storage
{
private:
    int m_nValue {};
    double m_dValue {};
public:
    Storage(int nValue, double dValue)
       : m_nValue { nValue }, m_dValue { dValue }
    {
    }

    // Make the Display class a friend of Storage
    friend class Display;
};

class Display
{
private:
    bool m_displayIntFirst;

public:
    Display(bool displayIntFirst)
         : m_displayIntFirst { displayIntFirst }
    {
    }

    void displayItem(const Storage& storage)
    {
        if (m_displayIntFirst)
            std::cout << storage.m_nValue << ' ' << storage.m_dValue << '\n';
        else // display double first
            std::cout << storage.m_dValue << ' ' << storage.m_nValue << '\n';
    }
};

int main()
{
    Storage storage(5, 6.7);
    Display display(false);

    display.displayItem(storage);

    return 0;
}

위 코드에서 Storage에 Display가 freind라는 선언을 해주었으므로
Display 클래스에서 Storage의 private member variable을 접근해 사용할 수 있음을 알 수 있다

Friend member functions

전체 클래스를 friend로 하는 것 말고도 다른 클래스의 특정 member function에만
friend 선언을 할 수도 있다
example:

class Display; // forward declaration for class Display

class Storage
{
private:
	int m_nValue {};
	double m_dValue {};
public:
	Storage(int nValue, double dValue)
		: m_nValue { nValue }, m_dValue { dValue }
	{
	}

	// Make the Display::displayItem member function a friend of the Storage class
	friend void Display::displayItem(const Storage& storage); // error: Storage hasn't seen the full definition of class Display
};

class Display
{
private:
	bool m_displayIntFirst {};

public:
	Display(bool displayIntFirst)
		: m_displayIntFirst { displayIntFirst }
	{
	}

	void displayItem(const Storage& storage)
	{
		if (m_displayIntFirst)
			std::cout << storage.m_nValue << ' ' << storage.m_dValue << '\n';
		else // display double first
			std::cout << storage.m_dValue << ' ' << storage.m_nValue << '\n';
	}
};

위 코드에서 friend void Display::displayItem(const Storage& storage);
와 같이 friend 선언을 해주는 것을 알 수 있다

그런데 위의 코드는 error가 발생한다
왜냐하면 Display 함수가 먼저 정의되지 않았기 때문이다
따라서 순서를 바꿔줘야 한다

class Display
{
private:
	bool m_displayIntFirst {};

public:
	Display(bool displayIntFirst)
		: m_displayIntFirst { displayIntFirst }
	{
	}

	void displayItem(const Storage& storage) // error: compiler doesn't know what a Storage is
	{
		if (m_displayIntFirst)
			std::cout << storage.m_nValue << ' ' << storage.m_dValue << '\n';
		else // display double first
			std::cout << storage.m_dValue << ' ' << storage.m_nValue << '\n';
	}
};

class Storage
{
private:
	int m_nValue {};
	double m_dValue {};
public:
	Storage(int nValue, double dValue)
		: m_nValue { nValue }, m_dValue { dValue }
	{
	}

	// Make the Display::displayItem member function a friend of the Storage class
	friend void Display::displayItem(const Storage& storage); // okay now
};

이렇게 하면 선언에 대한 문제는 해결할 수 있다
하지만 또 다른 문제가 발생한다
우리가 displayItem 함수에서 storage를 const reference로 받고 있기 때문에
Storage 클래스의 forward declaration이 필요하다
따라서 최종적으로 다음과 같이 작성할 수 있다

#include <iostream>

class Storage; // forward declaration for class Storage

class Display
{
private:
	bool m_displayIntFirst {};

public:
	Display(bool displayIntFirst)
		: m_displayIntFirst { displayIntFirst }
	{
	}

	void displayItem(const Storage& storage); // forward declaration above needed for this declaration line
};

class Storage // full definition of Storage class
{
private:
	int m_nValue {};
	double m_dValue {};
public:
	Storage(int nValue, double dValue)
		: m_nValue { nValue }, m_dValue { dValue }
	{
	}

	// Make the Display::displayItem member function a friend of the Storage class (requires seeing the full declaration of class Display, as above)
	friend void Display::displayItem(const Storage& storage);
};

// Now we can define Display::displayItem, which needs to have seen the full definition of class Storage
void Display::displayItem(const Storage& storage)
{
	if (m_displayIntFirst)
		std::cout << storage.m_nValue << ' ' << storage.m_dValue << '\n';
	else // display double first
		std::cout << storage.m_dValue << ' ' << storage.m_nValue << '\n';
}

int main()
{
    Storage storage(5, 6.7);
    Display display(false);

    display.displayItem(storage);

    return 0;
}

위의 작업은 매우 번거로워 보인다
하지만 실제로 모두 한파일에서 작업하지 않고 header file cpp file을 나눠서 작업하게 되므로 한결 낫다

Summary

friend 함수 또는 클래스는 마치 해당 클래스의 멤버인 것처럼 다른 클래스의 private 멤버에 액세스할 수 있는 함수 또는 클래스입니다. 이를 통해 friend 함수 또는 friend 클래스는 다른 클래스가 개인 멤버를 노출시키지 않고(예: 액세스 함수를 통해) 다른 클래스와 긴밀하게 작업할 수 있습니다.

Friending은 오버로드된 연산자를 정의할 때 일반적으로 사용되며(나중에 다룰 예정임), 둘 이상의 클래스가 친밀한 방식으로 함께 작업해야 할 때 덜 일반적입니다.

특정 멤버 함수를 friendfh 만들려면 멤버 함수의 클래스에 대한 전체 정의가 먼저 표시되어야 합니다.

profile
청룡동거주민

0개의 댓글