[C++] Pointer, Reference, Smart Pointer(Unique Pointer, Shared Pointer)

pos++·2023년 10월 20일

C++

목록 보기
4/5
post-thumbnail

2023.10.19 TIL

포인터

포인터 : 다른 변주나 함수의 주소를 담는 변수

포인터 변수도 쓰레기값이 담겨서 시작

포인터의 초기화

int *pointer1 = nullptr;
string *pointer2 = nullptr;

❗️ 다른 데이터타입의 주소는 담을 수 없다

💡 64bit OS : 포인터의 크기는 8Byte

cout << sizeof(pointer1) << endl;  // 8
int num = 0x64;
cout << &num << endl;  // 0x61fe14

int *pointer1 = &num;
cout << pointer1 << endl;  // 0x61fe14
cout << *pointer1 << endl;  // 0x64

int total = 300;
pointer1 = &total;

cout << pointer1 << endl;  // 0x61fe10
cout << *pointer1 << endl;  // 300

메모리 할당, 반환

heap 영역에서 동적으로 메모리를 할당받음

int *pointer = nullptr;
pointer = new int;
cout << pointer << endl;  // 0xe81a70
cout << *pointer << endl;  // 쓰레기값

*pointer = 20;
cout << *pointer << endl;  // 20

delete pointer;

배열의 할당, 반환

int* pointer = new int[3];

delete[] pointer;

포인터의 연산

int *pointer = 0x3000;
cout << pointer << endl;  // 0x3000
cout << pointer + 1 << endl;  // 0x3004
cout << pointer + 2 << endl;  // 0x3008

double *pointer = 0x3000;
cout << pointer << endl;  // 0x3000
cout << pointer + 1 << endl;  // 0x3008
cout << pointer + 2 << endl;  // 0x3016

실제로 3000이라는 값을 포인터변수에 담는법 :

int *pointer = reinterpret_cast<int*>(3000);

포인터 뺄셈

int main() {
		short** a = 8000;
		short** b = 0;

		cout << (b - a) << endl;  // 1000

		return 0;
}
  • b와 a 사이의 short* 변수의 개수를 반환한다.
  • 따라서 두 이중포인터간의 차이는 8000이고 포인터변수 short* 의 크기는 8이니까
    - 8000 / 8 = 1000

배열과 포인터의 관계

  • 배열의 이름은 포인터 변수와 유사
  • 배열은 시작 주소일 뿐 여러 데이터들의 집합이 아니다
  • 따라서 int 5개 배열에서 100번째 요소를 접근해도 문제가 없다
    • 그저 주소를 계산해서 그 위치를 반환할 것이다.
주소[index] == *(주소 + index)

const 포인터

const int *pointer;  // pointer가 가리키는 int값 변경불가
int *const pointer;  // pointer가 가진 주소값 변경불가, 그 주소가 가리키는 값은 변경 가능
const int *const pointer;  // pointer가 가라키는 int값과 주소값 변경불가
  • const 뒤에 붙는 것을 read-only로 바꿔준다

포인터 사용시 주의할점

  • 초기화된 포인터인지
    • 차라리 nullptr로 초기화 → OS를 가리켜 즉시 program crash
  • Dangling Pointer 문제가 발생하는지
    • 존재하지 않는 변수의 주소를 가리킬 경우 망함
    • delete된 Heap 영역을 가리키고 있을 경우 망함
    • 2개 이상의 포인터가 동일 영역을 가리킬 경우 망함
    • 하나의 예방법 : smart pointer 이용
  • new 연산이 fail하진 않을지
    • Memory는 유한하다
    • fail의 경우 메모리는 nullptr 반환
  • Memory Leak 가 발생하진 않는지
    • 컴퓨터 재시작 전까지는 반환되지 못하여 메모리 낭비 발생
    • 하나의 예방법 : smart pointer 이용

Reference

Pass by Reference

int sum(int &a, int &b) {
	int result = a + b;
	a = 200;
	a = 100;
	cout << a << " " << b << " " << result << endl;
	return result;
}

int main()
{
	int a = 100;
	int b = 200;
	int result = sum(a, b);
}
int num = 100;
cout << num << endl;

int &numReference;  // 초기화가 안됨. build error!
int &numReference = nullptr;  // build error!

int &numReference = num;  // num의 주소를 대입하는 것과 같은 기능
numReference = 200;
cout << num << endl;  // 200

int num2 = 300;
numReference = num2;

Reference 변수의 특징

  • 포인터 변수와 비슷함
  • 포인터 연산 없이 값을 참조하고 수정할 수 있는 const pointer
  • 초기화할때 특정 변수를 가리켜야 한다. nullptr 안됨!
  • 초기화한 이후 변경 불가
  • 파라미터로 사용하기에 적합

Reference를 사용한 성능 개선

string names[] = {"Mark", "Haechan", "Yuta"};
for (auto &name : names)
	cout << name << endl;

→ names의 주소를 공유! name에 값을 복사할 필요가 없다

Pass by Pointer

int sum(int *a, int *b) {
	int result = *a + *b;
	*a = 200;
	*b = 100;
	cout << a << " " << b << " " << result << endl;
	return result;
}

int main()
{
	int a = 100;
	int b = 200;
	int result = sum(a, b);
}

Smart Pointer

↔ raw pointer

  • Heap 영역을 가리키는 포인터
  • 자동으로 nullptr 초기화
  • 가리키는 영역을 참조하는 포인터가 없어지면 자동으로 delete 해주는 포인터
  • 역참조는 가능하지만 연산은 불가능

Unique Pointer

  • Heap 에 생성된 변수를 가리키는 포인터
  • 해당 변수를 가리키는 포인터는 하나뿐
  • Unique Pointer가 삭제될 때는 Heap 영역도 자동으로 delete 수행
  • p1.get() : 포인터 주소를 알려주는 함수
  • p1.reset() : pointer가 가지고 있던 Heap 반납

unique pointer의 생성

unique_ptr<int> p1 = make_unique<int>(100);

Shared Pointer

  • Heap 에 생성된 변수를 가리키는 포인터
  • 해당 변수를 가리키는 포인터 여러개 존재 가능
  • 몇개의 포인터 변수가 동일 Heap 영역을 가리키는지 count로 관리
    • p1.use_count()
  • p1.reset() : pointer가 가지고 있던 Heap 반납
shared_ptr<int> p1(new int(100));
shared_ptr<int> p1 = make_shared<int>(100);  // 더 효율적
vector<shared_ptr<int>> vec;
shared_ptr<int> p1(new int(100));
cout << p1.use_count() << endl;  // 1

vec.push_back(p1);  // p1의 복사본을 vec 변수가 가리킴
cout << p1.use_count() << endl;  // 2
profile
밀린 TIL 업로드 조금씩 정리중...

0개의 댓글