Love Every Moment

〔C++〕함수에서의 위치에 따른 const 의 의미 (부제: 함수의 반환값에 언제 const 를 붙이면 좋을까?) 본문

PROGRAMMING::LANGUAGE/C++

〔C++〕함수에서의 위치에 따른 const 의 의미 (부제: 함수의 반환값에 언제 const 를 붙이면 좋을까?)

해 송 2022. 7. 25. 19:01
반응형


0. 계기
42서울의 c++ 과제를 풀고나서 동료 평가를 받았는데, 되도록이면 모든 함수의 반환값을 const & 로 해주면 좋겠다는 조언을 받았다. 이유는 프로그램의 크기가 커질수록 참조자로 반환할 때와 아닐 때에 변수를 복사하는 데에 드는 비용이 높아지기 때문이라고 하셨다. 그 의견에 동의하면서도 모든 경우에 그렇게 처리하면 안 되는 이유를 분명히 C++ Primer Plus 원서에서 읽었던 기억이 나서, 이참에 어떤 케이스에서 const & 로 반환하고 어떤 케이스에선 일반 변수로 반환하는게 좋은지 정리하고자 한다. 그전에 const 키워드가 함수의 어디에 위치하는지에 따른 의미도 정리해보았다.



1. const 위치에 따른 의미
(1) 메서드 뒤의 const

class Foo{
	int num = 1;
    
    int getNum(void) const {
    	int a = 1;
  
        a++;   // 지역 변수는 가능
        num++; // Compile Error
        return num;
    }
 };
  • 해당 메서드가 속해있는 클래스의 멤버변수를 바꾸지 않겠다는 의미
  • getNum() 뒤에 const 가 붙었으므로 해당 함수가 속해있는 Foo 클래스의 멤버변수는 바꿀 수 없다
  • 하지만 Foo 의 멤버변수인 num 을 변경하려고 했으므로 컴파일 에러가 뜬다
  • 참고로 getNum() 안에서 선언한 지역 변수인 a 의 경우 수정해도 무방하다



(2) 함수의 매개변수에 붙은 const

#include <iostream>

int	add(const int a, const int b) {
	return (a + b);
}

int	main(void) {
	std::cout << add(1, 2) << std::endl;
}
  • 해당 매개변수를 함수 내에서 변경하지 않겠다는 의미
  • add() 함수는 단순히 a 와 b 를 합한 결과를 반환할 뿐, 내부에서 변경하지 않으므로 a, b 는 const 로 선언되었다
  • 하지만 add() 에서 a++; b++; 처럼 변수를 변경하여 사용할 필요가 있다면 const 키워드를 제거해야 할 것이다
  • 사실 여기서 a, b 는 pass by value 방식으로 넘겨받은 인자로부터 값을 복사하여 사용하기 때문에 내부에서 변경해봤자 원본 데이터에는 아무런 영향도 주지 않으므로 const 를 사용하는게 별로 의미는 없다

#include <iostream>

void	increase(int a) {
	a++; // 내부에서만 +1
}

int	main(void) {
	int	a = 1;

	increase(a); // 함수 내부에서만 매개변수가 +1 되고 원본 데이터는 그대로
	std::cout << a << std::endl; // 변경 없이 1 출력
}



(3) 함수의 매개변수에 붙은 const &

#include <iostream>

int	add(const int &a, const int &b) {
	return (a + b);
}

int	main(void) {
	int	a = 1;
	int	b = 2;

	std::cout << add(a, b) << std::endl;
}
  • 위의 (2)번에서는 const 가 단독으로 사용된 경우를 다루었는데 사실 (3)처럼 const & 를 함께 사용하는게 함수의 매개변수에 const 를 사용하는 가장 큰 이유라고 할 수 있다
  • call by reference 로 복사의 오버헤드 없이 변수를 참조하고, 해당 변수를 함수 내에서 변경하지 않겠다는 의미
  • 이렇게 처리하면 일반적인 call by value 방법으로 매개변수에 인자를 넘겨주었을 때의 데이터 복사가 발생하지 않고, 매개변수는 넘겨받은 인자의 참조자가 되기에 메모리 낭비를 줄일 수 있다
  • 이 때, 참조자의 특성 상 매개변수를 변경하면 원래 넘겨받은 데이터까지 변경될 가능성이 있으므로 이를 방지하기 위해 const 를 같이 사용한다



(4) 함수 앞에 붙은 const

  • 함수의 반환값을 상수화 시키겠다는 의미
  • 이것 또한 앞의 (2), (3)번에서 다룬 내용과 동일하게 생각하면 편하다
  • 반환값이 변경되면 안 되는 경우(=lValue 가 되면 안 되는 경우)에 사용하면 된다
  • 그런데 빌트인 타입의 값을 반환하는 경우, 어차피 변수가 아니라서 컴파일러가 자동으로 lValue가 되는 것을 방지해주므로 굳이 const 를 붙여서 반환할 필요 없다
  • 예를 들자면 아래와 같이 함수의 반환값을 바로 수정하는 이상한 경우를 방지하는 것인데
#include <iostream>

int	add(const int &a, const int &b) {
	return (a + b);
}

int	main(void) {
	int	a = 1;
	int	b = 2;

	std::cout << ++add(a, b) << std::endl;
}
  • add() 의 반환값에 const int 를 붙여서 ++add(a, b) 와 같이 반환값을 변경하는 것을 방지해야할 것 같지만, 그렇게 하면 컴파일러가 자체적으로 '어차피 lValue 가 될 수 없는 값을 수정할 수도 없는데 const int 로 반환하는건 아무 의미 없다'고 에러를 띄워준다


  • 그렇다면 어떤 경우에 함수 앞의 const 가 의미 있어지는가 하면 (3)번처럼 참조자(&)와 함께 쓸 경우이다
  • 이 때 주의해야 할 점은, 반환되는 변수가 함수 내부에서 선언된 지역 변수가 아니어야 한다는 것이다
  • 함수가 종료하는 시점에 지역 변수도 소멸되기 때문에, 참조자로 반환했다가는 dangling reference 가 되고 만다
  • 그래서 이런 경우는 참조자나 const 없이 그냥 int, float 등으로 반환해준다

class Bureaucrat {
private:
	const std::string	name_;
	int			grade_;

public:
	Bureaucrat();
	Bureaucrat(const std::string &name, int grade);
	Bureaucrat(const Bureaucrat &origin);
	Bureaucrat &operator=(const Bureaucrat &origin);
	~Bureaucrat();

	const std::string	&getName() const;
	const int			&getGrade() const;
};
const std::string	&Bureaucrat::getName() const {
	return (name_);
}

const int	&Bureaucrat::getGrade() const {
	return (grade_);
}
  • const & 형태로 반환하는 경우는 보통 참조자로 받은 매개변수를 함수에서 사용하고 그대로 다시 참조자로 반환하거나,
  • 해당 메서드가 속해있는 클래스의 멤버 변수여서 참조자로 반환하더라도 dangling 되지 않는 경우 등이 있다
  • 위의 예시는 두 번째 케이스에 속하는 것으로 클래스의 멤버 변수를 const &로 반환하는 경우이다
  • 그냥 std::string 로 반환해도 상관은 없지만 메모리 사용상의 이점상 이렇게 하는 것을 추천한다
  • (22.01.24 추가) int 의 경우 built-in primitive type 이어서 복사를 하는 데에 비용이 크지 않기 때문에, 주소를 참조하러 가는 비용보다 작아서 복사본을 반환하는 것이 낫다.


C++ Return value, reference, const reference

Can you explain to me the difference between returning value, reference to value, and const reference to value? Value: Vector2D operator += (const Vector2D& vector) { this->x += vector...

stackoverflow.com

[C++] 함수 앞에 쓰이는 const 키워드는 어떤 의미인가요? | KLDP

const int func() { ... } 위와 같이 함수 앞에 const 키워드가 쓰일 때가 있던데요. 무슨 의미인지 궁금합니다.

kldp.org



반응형
Comments