1장 C++와 표준 라이브러리 초단기 속성 코스(2)

myeongrangcoding·2023년 11월 27일
0

전문가를 위한 C++

목록 보기
2/8

레퍼런스

  • 변수에 대한 앨리어스(별명).
  • 레퍼런스 변수, 레퍼런스 데이터 멤버, 레퍼런스 매개변수, 레퍼런스 리턴값.

레퍼런스 변수

  • 생성 즉시 초기화.
  • 가리키는 대상을 바꿀 수 없고, 그 레퍼런스가 가리키는 변수의 값만 바꿀 수 있다.
  • 정수 리터럴이나 임시 객체에 대해 비 const 레퍼런스는 만들 수 없지만 const 레퍼런스는 만들 수 있다.
  • 레퍼런스는 모든 타입에 대해 만들 수 있지만 레퍼런스에 대한 레퍼런스는 선언할 수 없다.
int* intPtr{nullptr};
int*& ptrRef{intPtr};
ptrRef = new int;
*ptrRef = 5;

//int& & 	불가능.
//int&* 	불가능.
  • const 변수와 레퍼런스를 구조적 바인딩에 적용할 수 있다.

레퍼런스 데이터 멤버

  • 생성 즉시 초기화해야 하기 때문에 생성자의 본문이 아닌 생성자 초기자에서 초기화해야 한다.

레퍼런스 매개변수

  • 가장 많이 사용된다.
  • 레퍼런스로 전달하는 매개변수에 리터럴을 지정하려면 const 레퍼런스로 전달한다.
  • 함수에서 객체를 리턴하는 바람직한 방법은 출력 매개변수를 사용하는 것이 아니라 값으로 리턴하는 것이다.

개발자의 관점에서 볼 때 객체를 직접 리턴하지 않고 함수 내부에서 원본 객체를 직접 수정하는 방식으로 처리하면 복제로 인한 성능 저하를 줄일 수 있어서 바람직하다고 생각하기 쉽다. 하지만 컴파일러는 이미 예전부터 이렇게 불필요한 복제 작업을 제거해왔다.
(중략)
전달할 객체를 함수 안에서 수정해야 할 때는 비 const 레퍼런스로 전달한다. 단, 9장에서 소개하는 이동 의미론을 이용한다면 객체를 값으로 전달해도 효율적으로 처리된다.

return object;와 같이 작성하면 object가 로컬 변수거나, 함수에 대한 매개변수거나, 임싯값일 때 리턴값 최적화(RVO)가 적용된다. 또한 object가 로컬 변수라면 이름 있는 리턴값 최적화(NRVO)도 적용된다. RVO와 NRVO 둘 다 복제 생략의 한 종류로서, 함수에서 객체를 리턴하는 과정을 굉장히 효율적으로 처리해준다. 복제 생략을 사용하면 컴파일러는 함수에서 리턴하는 객체를 복사하지 않는다. 이를 통해 복제 없는 값 전달 방식을 구현한다. (145p)

레퍼런스 리턴값

  • 주된 경우는 리턴값을 lvalue에 직접 대입할 때.

레퍼런스와 포인터

  • 레퍼런스는 포인터보다 훨씬 안전하다. 레퍼런스의 값은 널이 될 수 없고, 레퍼런스를 명시적으로 역참조(*)할 수도 없다.
  • 객체에 대한 레퍼런스는 다형성도 지원한다.
  • 반드시 포인터를 사용해야 하는 경우.
    1. 동적 할당 메모리 주소 저장.
    2. 주솟값이 nullptr이 될 수도 있는 optional.
    3. 컨테이너에 다형성 타입을 저장할 때.(가리키는 대상을 바꿀 수 없다.)

익셉션

  • 익셉션을 던진다: 특정한 조건을 만족해서 익셉션을 발생시키는 것. throw 구문으로 작성.
  • 익셉션을 잡는다: 발생된 익셉션에 대해 적절한 동작을 수행하는 것. catch 문으로 작성.
  • 익셉션은 const 레퍼런스로 잡는 것이 바람직하다.
  • what() 메서드로 익셉션에 대한 간략한 설명을 담은 스트링을 리턴할 수 있다.
#include <iostream>
#include <stdexcept>

using namespace std;

int getInt(int number)
{
	if (number == 2)
	{
		throw invalid_argument("Number cannot be 2.");
	}
	return number;
}

int main()
{
	try
	{
		cout << getInt(1) << endl;
		cout << getInt(2) << endl;
		cout << getInt(3) << endl;
	}
	catch (const invalid_argument& exception)
	{
		cout << "Exception caught: " << exception.what() << endl;
		//cout << "Exception caught: {} ", exception.what() << endl;
	}
}

// 출력.
// 1
// Exception caught : Number cannot be 2.

타입 앨리어스

  • using string = basic_string<char>
  • typedef보다는 타입 앨리어스(using)를 사용.

타입 추론

  • 표현식의 타입을 컴파일러가 스스로 알아내는 기능.
  • auto, decltype.

auto 키워드

  • 함수의 리턴 타입 추론.
  • 구조적 바인딩에 사용.
    auto[]{}
  • 표현식의 타입 추론.
  • non-type 템플릿 매개변수의 타입 추론.
  • 축약 함수 템플릿 구문.
  • decltype(auto).
  • 함수에 대한 또 다른 문법으로 사용.
  • 제네릭 람다 표현식에서 사용.

auto&

  • auto를 지정하면 레퍼런스와 const 한정자가 사라진다.
  • as_count() 유틸리티 함수는 레퍼런스 매개변수에 대해 const 레퍼런스를 리턴한다. 그런데 as_count()와 auto를 조합할 때 auto는 레퍼런스와 const를 제거하기 때문에 const auto&로 지정해야 한다.

auto*

  • 포인터에도 적용할 수 있지만 포인터를 다룰 때는 auto* 구문을 사용하는 것이 바람직하다.
// const int* 라고 생각하겠지만 int* const다.
const auto p1{&i};
auto const p2{&i};

// const int*다.
const auto* p3{&i};
// int* const다.
auto* const p4{&i};

복제 리스트 초기화와 직접 리스트 초기화

  • 복제 리스트 초기화: T t = {arg1, arg2, ...}; // 타입이 모두 같아야 한다.
  • 직접 리스트 초기화: T t {arg1, arg2, ...};

decltype 키워드

  • 인수로 전달한 표현식의 타입을 알아내는 키워드.
  • 레퍼런스나 const 지정자를 삭제하지 않는다.
int a {123};
decltype(a) b {456};	// 컴파일러는 b의 타입이 a의 타입인 int라고 추론한다.
decltype(foo()) f2{foo()};
profile
명랑코딩!

0개의 댓글