C++/C++ 심화 강좌

[C++11/17 심화 강좌] 스마트 포인터로 배열 동적할당하기

카루-R 2022. 2. 16. 13:12
반응형

결론: C++11에선 걍 std::vector 쓰세요.

std::shared_ptr로 배열을 동적할당 해보겠습니다.

| 코드 1 - 잘못된 배열 생성 |

// [코드 1: 잘못된 배열 생성]
// C++11이상에서 동작합니다.
#include <iostream>
#include <memory>
using namespace std;

int main() {
	struct A { 
		A() { cout << "A()" << endl; }
		~A() { cout << "~A()" << endl; }
	};
	shared_ptr<A> array(new A[10]);
}

너무나 실행이 잘됩니다. 그러나, 치명적인 문제가 있습니다.

10개의 요소를 생성했으나 맨 처음의 한 개만 제거되고 나머지는 그대로 남게됩니다.

클래스 내부에서 delete[] 가 아닌 delete로 삭제했기 때문이죠.

이를 위해, shared_ptr는 배열을 삭제할 수 있는 방법을 줍니다.

| 코드 2 - 올바른 배열 생성 (1) |

// [코드 2: 올바른 배열 생성 (1) ]
// C++11이상에서 동작합니다.
#include <iostream>
#include <memory>
using namespace std;

int main() {
	struct A { 
		A() { cout << "A()" << endl; }
		~A() { cout << "~A()" << endl; }
	};
	shared_ptr<A> array(new A[10], [](A* a) {
		 delete[] a; 
	});
}

예, 이제 해제가 아주 잘 됩니다. 하지만 저렇게 매번 타입을 적기는 귀찮습니다.

C++14의 제네릭 람다를 이용하면 어떨까요?

shared_ptr<A> array(new A[10], [](auto a) { delete[] a; })

auto, 좋습니다. 아주 좋아요.

하지만! 이렇게 람다를 매번 정의하고 넘기는것 자체가 귀찮습니다.

그래서 C++17에선 파격적인 문법을 제공합니다.

타입을 T가 아닌 T[]로 기술하는 겁니다.

| 코드 3 - 올바른 배열 생성 (2) |

// [코드 3: 올바른 배열 생성 (2) ]
// C++17이상에서 동작합니다.
#include <iostream>
#include <memory>
using namespace std;

int main() {
	struct A { 
		A() { cout << "A()" << endl; }
		~A() { cout << "~A()" << endl; }
	};
	shared_ptr<A[]> array(new A[10]);
}

타입도 적기 귀찮지 않나요? 소스코드 위에 이것들을 적어주세요.

template<class T> shared_ptr(T*) -> shared_ptr<T>;
template<class T> shared_ptr(T*[]) -> shared_ptr<T[]>;

이걸 적으면 다음과 같이 템플릿 인자를 아예 생략해도 알아서 만들어줍니다.

shared_ptr array(new A[10]);

그러니까, shared_ptr array(new A[10]) 이나, vector array(10)이나 다 거기서 거기라고 볼 수 있습니다. 차이점이라면 배열과 포인터 정도?

위 코드에 대한 설명은 아래 링크를 참고하세요.

 

[C++17 심화 강좌] 스마트 포인터 편리하게 사용하기

장렬히 떠나신 auto_ptr에 대한 내용은 기술하지 않겠습니다. 평소에 자주 쓰는 스마트 포인터는 shared_ptr, unique_ptr 이 둘뿐이라고 생각합니다. shared_ptr p(new int[10], [](int* p){ delete[] p; }); 아마..

karupro.tistory.com

 

반응형