references

😎·2022년 12월 13일
0

CPP

목록 보기
15/46

references __ 참조

C 의 포인터와 비교해보자. C 는 특정 스코프에서 스택이 아닌 힙 영역에 동적 할당하고, 주소값을 이용해서 조작한다.

우리는 굉장히 많은 애스터리스크(*)와 앤퍼센트(&)를 사용한다. 좋지만 실용적이진 않다. C++ 에는 비슷한 개념인 references 가 있다.

쉽게 const 로 된 포인터이며, 포인터보다 사용 방법이 쉽다고 생각하자. references 는 참조라고 표현하겠다.


예시

예시를 보면 설명을 더 해보자.

#include <iostream>

	 int main(void)
	 {
1.     int numberOfBalls = 42;

2.     int *ballsPtr = &numberOfBalls;
3.     int &ballsRef = numberOfBalls;
4.     // int &ballsRef2 ; // 동작 안함

5.     std::cout << numberOfBalls << " " << *ballsPtr << " " << ballsRef << std::endl;

6.     *ballsPtr = 21;
7.     std::cout << numberOfBalls << std::endl;
8.     ballsRef = 84;
9.     std::cout << numberOfBalls << std::endl;

    return (0);
}

1 번에서 numberOfBalls 를 선언했다. 2 번에서는 C 에서 많이 사용했던 방식으로 포인터를 만들어서 numberOfBalls 의 주소값을 가리키도록 했다.

참조는 3 번에 있다. 선언은 &변수명으로 한다. 이때 처음에 얘기했던 것처럼 const 변수와 같이 초기화를 바로 해줘야한다. ballsRefnumberOfBalls 참조하고 있고, numberOfBalls 의 별명으로 이해하자.

4 번처럼 초기화 없이 변수를 선언하면 다음과 같은 에러가 생긴다.

5 번은 일반 변수, 포인터 변수, 참조 변수의 값을 순서대로 출력한다. 여기서 값에 접근할 때 참조 변수는 포인터가 애스터리스크(*)를 사용하는 것과 다르게, 일반 변수와 같이 접근한다. 즉, 아무것도 붙이지 않고 변수만 있다.

값은 어떻게 바꿀 수 있을까? 6, 7 번은 우리가 익히 알고 있는 포인터의 값을 바꾸는 방법이다. 8, 9 번은 참 변수의 값을 바꾸는데, 일반 변수를 바꾸는 방법과 같다.

어떤가? C 의 포인터보다 다루기 쉽지 않은가? 위 코드의 결과는 다음과 같다.


매개 변수로 참조를 이용할 때?

매개 변수에 & 를 붙여주는 것 외에는 일반 변수를 사용하는 것과 방법이 같다.

#include <iostream>
#include <string>

void    byRef(std::string &str)
{
    str += " and ponies";
}

// 값을 바꾸는게 아니기 때문에 const 매개변수로 받는다.
void    byConstRef(std::string const &str)
{
    std::cout << str << std::endl;
}

int main(void)
{
    std::string str = "i like otters";

    std::cout << str << std::endl;
    byRef(str);
    byConstRef(str);

    return (0);
}


반환값으로 참조를 이용할 때?

메모리 관리!!를 위해 매개 변수와 반환은 값보다는 메모리 주소를 사용한다.

특징1 __ 반환값

반환값이 변수의 값이 아닌 변수의 메모리 주소다. 참조 반환 또는 포인터 반환. 왜?? 변수의 값을 반환하면 새로운 객체를 생성하면서 반환한다고 한다. 반환값은 상황에 따라 메모리 주소나 값을 사용한다. 하지만 아래 코드에서는 굳이 값을 반환할 이유가 없기 때문에 주소를 반환한다.

특징2 __ 매개 변수

객체를 생성하며 생성자가 호출될 때, 생성자의 매개 변수가 어떤 형태로 들어가는지 잘 확인하자. 아래 코드는 const &login 의 형태, 즉 참조로 넣어줬다. 매개 변수를 참조로 넣은 이유는 값으로 넣을 경우, 복사생성자를 호출하며 불필요한 자원 낭비가 생기기 때문이다.

C++ 에서 매개 변수는 대부분 주소로 받는다.

#include <iostream>
#include <string>

class Student
{
    private:
        std::string _login;

    public:
        Student(std::string const &login) : _login(login)
        {
        }

        std::string&    getLoginRef()
        {
            return this->_login;
        }

        std::string const & getLoginRefConst() const
        {
            return this->_login;
        }

        std::string*    getLoginPtr()
        {
            return &(this->_login);
        }

        std::string const * getLoginPtrConst() const
        {
            return &(this->_login);
        }
};

int main(void)
{
    Student         bob = Student("bfubar");
    Student const   jim = Student("jfubar");

    std::cout << bob.getLoginRefConst() << " " << jim.getLoginRefConst() << std::endl;
    std::cout << *(bob.getLoginPtrConst()) << " " << *(jim.getLoginPtrConst()) << std::endl;

    bob.getLoginRef() = "bobfubar";
    std::cout << bob.getLoginRefConst() << std::endl;

    *(bob.getLoginPtr()) = "bobbyfubar";
    std::cout << bob.getLoginRefConst() << std::endl;
}


포인터와 참조는 어떤 상황에서 사용해야할까?

객체를 생성할 때 멤버 변수의 값이 추후에 바뀌는지 안바뀌는지에 따라 포인터 또는 참조를 사용한다. 예를 들어 RPG 캐릭터가 있다고 하자. A 캐릭터는 무기를 가질 수도 있고, 안 가질 수도 있다. B 캐릭터는 무기를 가져야만 한다.

이런 상황이라면 A 캐릭터는 포인터를 사용해야하고, B 캐릭터는 참조를 사용해야한다.

profile
jaekim

0개의 댓글