Plite
전자오락 공방
Plite
전체 방문자
오늘
어제
  • 분류 전체보기 (274)
    • 프로젝트 (18)
      • 완성 프로젝트 (3)
      • 프로젝트 진행 내역 (15)
    • 공부 및 정리 (241)
      • 백준 코드 (222)
      • C++ (8)
      • DirectX (2)
      • Unreal Engine (6)
      • 프로그래밍 패턴 (3)
    • 기타 (12)
      • 기타 주저리 (10)
    • 게임과 취미 (1)
    • 대문 (1)

블로그 메뉴

  • 홈
  • 프로젝트
  • 취미, 일상
  • 백준 프로필

공지사항

  • [Read Me]
  • 제 블로그에 방문하신 것을 환영합니다.

인기 글

태그

  • 구현
  • 브루트포스
  • 누적합
  • 큐
  • 백준
  • LCA
  • UC++
  • 투포인터
  • 그래프
  • 이분탐색
  • 백트래킹
  • 트라이
  • C++
  • 세그먼트 트리
  • 위상 정렬
  • KMP
  • 기하
  • 수학
  • 정렬
  • 정수론
  • 분할정복
  • 문자열
  • 스택
  • 트리
  • 우선순위큐
  • 유니온 파인드
  • 동적계획법
  • 조합론
  • SCC
  • 최소 스패닝 트리

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Plite

전자오락 공방

공부 및 정리/C++

C++의 기초 - 6

2022. 12. 12. 20:29

* 참조자

 

int A = 99;
int &B = A;
int *C = &A;

B++;
//A == 100

(*C)++;
//A == 101

 

- 참조자는 포인터가 *를 사용해 데이터를 실제로 변경하는 것처럼 해당 데이터에 직접 접근할 수 있습니다.

 

int A = 99;
int E = 1;
int &B = A;
int* C = &A;
int& D = E;


B++;
//A == 100
(* C)++;
//A == 101

printf("%d\n", A);	//101
printf("%p\n", &B);	//A의 주소
printf("%p\n", C);	//A의 주소	
printf("%d\n", E);	//1

B = D;
//A == 1

B--;
//A == 0

printf("%d\n", A);	//0
printf("%p\n", &B);	//A의 주소
printf("%p\n", C);	//A의 주소
printf("%d\n", E);	//1

 

- B가 A의 참조자이므로 B = D를 수행한 순간 D가 E의 참조자이므로 A = E가 이루어집니다.

- A--;를 했으므로 A의 값은 0입니다.

- E의 값은 변하지 않아서 1입니다.

 

 

* 매개변수로서의 참조자

 

void func(int &A)
{
    A *= A;
}

void func_ref(int *A)
{
    *A *= *A;
}

void func_ref_con (int const &A)
{
	A * A;
}

int const& func_ret_con(int const&A)
{
	return A * A;
}

int main()
{
    int a;
    func(a); //a의 값에 실제로 영향을 줌
    func_ref(&a); //a의 값에 실제로 영향을 줌
    func(a + 3);	//컴파일 에러, 불가능
    func_ref(&(a + 3)); //컴파일 에러, 불가능
    
    func_ref_con(a + 3); //const & 매개변수의 경우 임시 변수를 넘길수 있으나, a를 변경할 수 없음
    int c = func_ret_con(a + 5); //임시 변수를 넘길 수 있고, c에 임시 변수를 Return할 수 있음
    return 0;
}

 

- 참조자를 통해 매개변수로 넘어간 변수의 값이 실제로 영향을 받게 됩니다. 

- 포인터를 통해 매개변수로 넘어간 변수 역시 영향을 받지만, *연산자를 계속 써줘야 합니다.

- 임시로 생성되는 값을 매개변수로 넘길 수 없게 됩니다.

- const &를 사용하는 매개변수는 임시 변수를 넘길 수 있으나, 기존 변수의 값을 변경할 수는 없습니다.

- const &를 반환하는 함수는 임시 변수를 반환할 수 있으며, 기존 반환 방식보다 효율적입니다. (복사 한 번이 생략됩니다.)

 

- 결론적으로 참조를 사용하는 이유를 꼽자면

 

1. 매개 변수의 데이터 변경을 위해서

2. 함수 호출의 효율성 증가를 위해서

 

정도로 볼 수 있겠습니다.

 

[참고] 함수에서 배열 형태의 매개변수를 변경하려면 포인터를 써야 합니다.

 

여기서 더 깊은 이해를 하기 위해서는 l-value와 r-value를 알아야 하는데, 다음에 알아보도록 하겠습니다.

 

 

* 일반화 프로그래밍

- 여러가지 데이터 형을 포함하여 일반형으로 프로그래밍 하는 것을 일반화 프로그래밍이라 합니다.

 

- C++은 함수의 다형성을 지원하기는 하지만, 같은 함수를 타입만 바꿔서 복사하는 대신, 템플릿 함수를 이용합니다.

 

#include <iostream>

template <typename T>
void swap(T &A, T &B)
{
	T temp;
	temp = A;
	A = B;
	B = temp;
}


int main()
{
	int a= 0 , b = 2;
	swap(a, b);
	printf("%d %d", a, b);


	return 0;
}

 

- swap 함수는 이제, 여러 데이터 형을 지원하는 함수가 되었습니다.

- typename 대신 class를 사용해도 동일하게 동작합니다.

- 컴파일러 단계에서는 템플릿 함수를 호출할 때, 해당 매개변수에 맞는 함수를 새로 생성합니다.

 

예시) Swap((int)a, (int)b) -->   Swap(int &, int &) {}

Swap((double)a, (double)b) --> Swap(double&, double&) {}

 

즉, 최종 단계의 코드에서는 템플릿 함수는 없고 호출된 만큼의 같은 이름의 함수가 여러개 존재하게 됩니다.

 

또한, 템플릿 함수 역시 오버로딩이 가능합니다.

 

예시)

template <typename T>

swap(T &A, T&B)

 

templata <typename T>

swap(T *A, T*B, int C)

 

 

 

* 함수의 구체화/ 특수화

 

- 구체화는 호출하는 측에서 함수를 구체화 하는 것을 의미합니다.

- 특수화는 정의하는 측에서 함수를 명시하는 것을 의미합니다.

 

 

* 암시적 구체화

 

template 함수는 받는 자료형에 따라 어떤 함수가 호출될 지 모릅니다.

 

하지만, 우리가 Swap(4, 5)와 같이 int 형을 넣어주게 되면

 

Swap 함수는 int로 구체화 되어 동작합니다.

 

이를 구체화라고 합니다.

#include <iostream>
template<typename T>
void func(T &a)
{
   printf("%d \n", sizeof(a));
}

template<typename T>
void func(T&& a)
{
    printf("%d \n", sizeof(a));
}

int main()
{
    func(4);		//4
    func(4.0l);		//12
    return 0;
}

 

- 암시적 구체화는 직접 자료형을 명시하지 않아도, 매개변수를 보고 암시적으로 함수를 구체화하는 것을 의미합니다.

 

- 위 예시를 보시면, func에 들어간 매개변수가 int와 long double이므로 다르게 동작하는 것을 볼 수 있습니다.

 

* 명시적 구체화

 

- 템플릿 함수는 호출 시 우리가 <> 괄호 안에 타입을 명시적으로 적어줄 수 있습니다.

 

- 이와 같이 직접 매개변수 타입을 명시하여 구체화하는 것을 명시적 구체화라 합니다.

#include <iostream>
template<typename T>
void func(T &a)
{
   printf("%d \n", sizeof(a));
}

template<typename T>
void func(T&& a)
{
    printf("%d \n", sizeof(a));
}

int main()
{
    func<double>(4);	//8
    return 0;
}

 

위 예시는 아까와 동일하게 int 4를 매개변수로 넣어주지만, double 형 매개변수라고 명시해주었습니다.

 

따라서, 받는 함수는 매개변수를 double로 취급하여 8을 출력합니다.

 

 

* 명시적 특수화

 

- 만약, 템플릿 함수가 어떤 자료형에는 다른 동작을 하길 원한다면 명시적 특수화를 이용해볼 수 있습니다.

 

#include <iostream>

template <typename T>
void swap(T &A, T &B)
{
	T temp;
	temp = A;
	A = B;
	B = temp;
}

template <>
void swap(char& A, char& B)
{
	return;
}

int main()
{
	int a= 0 , b = 2;
	swap(a, b);
	printf("%d %d\n", a, b);	//2 0

	char c = '3';
	char d = '4';

	swap(c, d);
	printf("%c %c", c, d);	//3 4

	return 0;
}

 

- 위와 같은 코드에서는, swap 함수가 char 자료형을 받았을 때에만 swap하지 않도록 했습니다.

 

 

#include <iostream>

template <typename T>
void swap(T &A, T &B)
{
	T temp;
	temp = A;
	A = B;
	B = temp;
}

void swap(int& A, int& B)
{
	return;
}

int main()
{
	int a= 1 , b = 2;
	swap(a, b);
	printf("%d %d\n", a, b);	//1 2

	int c = 3;
	int d = 4;
	swap<>(c, d);
	printf("%d %d", c, d);	//4 3

	return 0;
}

 

- 같은 int형으로 호출함에도, swap<>(c, d); 구문은 템플릿 함수를 호출했습니다.

 

- 함수가 서로 다른 템플릿 자료형 2개를 받아 리턴해야 할 경우에는 함수를 만들기 어렵습니다.

 

 

 

* decltype()

 

decltype()은 ()안의 결과를 타입으로 사용하는 함수입니다.

 

어떤 타입이 될지 모르는 경우에 쓸 수 있습니다.

 

#include <iostream>

template <typename T1, typename T2>
auto plus(T1 &A, T2 &B)
{
	typedef decltype(A + B) rettype;
	rettype APB = A + B;
	return APB;
}
int main()
{
	int a = 1;
	double b = 2;
	double C = plus(a, b);

	return 0;
}

 

- 위와 같은 경우, 임의의 매개 변수 2개를 더했을 때, 어떤 타입으로 결정날 지 알 수 없습니다.

- 함수 내에서는 decltype() 구문을 이용해, 결정된 타입을 사용하는 변수를 쓸 수 있습니다.

- 함수의 return형의 경우, 어떤 타입이 나올 지 알 수 없는 관계로 auto를 이용합니다.

 

 

저작자표시 (새창열림)

'공부 및 정리 > C++' 카테고리의 다른 글

[메모] l-value와 r-value  (0) 2022.12.20
C++의 기초 - 7  (0) 2022.12.20
C++의 기초 - 5  (0) 2022.12.12
C++의 기초 - 4  (0) 2022.11.30
C++의 기초 - 3  (0) 2022.07.12
    '공부 및 정리/C++' 카테고리의 다른 글
    • [메모] l-value와 r-value
    • C++의 기초 - 7
    • C++의 기초 - 5
    • C++의 기초 - 4
    Plite
    Plite
    개발 일지, 게임 이야기 등을 적어두는 공간입니다.

    티스토리툴바