unique_ptr은 동적으로 할당된 객체를 유지할 경우 객체가 소멸하면 클래스가 자동으로 delete를 호출하기 때문에 쓸데없는 메모리가 나가는것을 막을 수 있다.
unique 포인터는 가리키는 객체에 대한 소유권을 표현하며 해당 객체를 다시 사용하지 않을 경우 이와 관련된 메모리를 비우는 역할을 한다.
전체 코드는 아래와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
#include<iostream>
#include<memory>
#include<vector>
class A {
int *ptr;
public:
A() {
std::cout << "A 자원 획득!" << std::endl;
ptr = new int[100];
}
A(int a) {
std::cout << "A : 인자 있는 자원 획득!" << std::endl;
ptr = new int[100];
ptr[0] = a;
}
void some() { std::cout << "A : 일반 포인터와 동일하게 사용가능??" << std::endl; }
void some2() { std::cout << "A의 some2 값 : " << ptr[0] << std::endl; }
void dosome(int a) {
std::cout << "A 뭔가를 한다." << std::endl;
ptr[0] = a;
}
~A() {
std::cout << "A 소멸자 호출(자원 해제)" << std::endl;
delete[] ptr;
}
};
class B {
int a, b;
public:
B(int a, int b) : a(a), b(b) { std::cout << "B의 인자있는 생성자 호출" << std::endl; }
void print() { std::cout << "a : " << a << ", b : " << b << std::endl; }
~B() { std::cout << "B 소멸자 호출" << std::endl; }
};
void f() {
std::unique_ptr<A> a(new A()); // 이거는 a는 A클래스릐 객체를 가리키는 포인터, 마치 A* a = new A() 와 같다.
std::cout << "a : ";
a->some();
std::cout << "\n\n";
//위와 같이 a가 포이터인것처럼 사용, a는 스택에 정의된 객체, f() 함수가 종료될때, 자동으로 소멸자 호출
//unique_ptr은 소멸자 안에서 자시닝 가리키고 있는 자원을 해제함.
//b에 소유권을 이전한다. 이동 생성자로 사용가능 (복사는 안됨)
std::unique_ptr<A> b = std::move(a);
std::cout << "b : ";
b->some();
std::cout << "\n\n";
//a를 b에 강제로 이동시킨다. b가 new A 로 생성된 객체의 소유권을 갖게 되고 a는 아무것도 가리키지 않는다.
//이제 기존의 unique_ptr인 a는 접근하지 말자
}
void f2(A* pointer) { pointer->dosome(4); }
int main() {
f();
std::unique_ptr<A> a(new A());
f2(a.get());
//유일하게 소유한 객체를 지키기위해 함수 인자로는 원래의 포인터 주소값만을 전달한다.
//get : 실제 객체의 주소값 리턴
std::cout << "\n\n";
auto p = std::make_unique<B>(3, 5);
p->print();
//템플릿 인자로 전달된 클래스의 생성자에 인자들에 직접 완벽한 전달을 수행
std::cout << "\n\n";
std::vector<std::unique_ptr<A>> arr;
//arr.push_back(std::unique_ptr<A>(new A(1)));
arr.emplace_back(new A(1)); // 위와 동일한 문장
//emplace_back 함수로 전달된 인자를 완벽한 전달을 통해, 직접 unique_ptr<A>의 생성자에 전달해
//vector의 맨뒤에 unique_ptr<A> 객체를 생성
std::cout << "\n\n";
return 0;
}
/*
특정 개체에 유일한 소유권을 부여하는 포인터 객체를 uniue_ptr
unique_ptr 은 어떤 객체의 유일한 소유권을 나타내는 포인터 이며, unique_ptr 가 소멸될 때, 가리키던 객체 역시 소멸된다.
만약에 다른 함수에서 unique_ptr 가 소유한 객체에 일시적으로 접근하고 싶다면, get 을 통해 해당 객체의 포인터를 전달하면 된다.
만약에 소유권을 이동하고자 한다면, unique_ptr 를 move 하면 된다.
*/
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
|
unique_ptr
먼저의 아래의 코드부분만 보자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
class A {
int *ptr;
public:
A() {
std::cout << "A 자원 획득!" << std::endl;
ptr = new int[100];
}
A(int a) {
std::cout << "A : 인자 있는 자원 획득!" << std::endl;
ptr = new int[100];
ptr[0] = a;
}
void some() { std::cout << "A : 일반 포인터와 동일하게 사용가능??" << std::endl; }
void some2() { std::cout << "A의 some2 값 : " << ptr[0] << std::endl; }
void dosome(int a) {
std::cout << "A 뭔가를 한다." << std::endl;
ptr[0] = a;
}
~A() {
std::cout << "A 소멸자 호출(자원 해제)" << std::endl;
delete[] ptr;
}
};
void f() {
std::unique_ptr<A> a(new A()); // 이거는 a는 A클래스릐 객체를 가리키는 포인터, 마치 A* a = new A() 와 같다.
std::cout << "a : ";
a->some();
std::cout << "\n\n";
}
int main() {
f();
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
|
위 코드의 실행 결과는 아래와 같다.
unique_ptr 를 정의하기 위해서는 템플릿에 인자로, 포인터가 가리킬 클래스를 전달하게 한다. a가 포인터인것처럼 사용하면 된다.. unique_ptr 은 -> 연산자를 오버로드해서 마치 포인터를 다루는 것과 같이 사용할 수 있다.
이번에는 소유권을 이전해보자. 위의 코드에서 아래만 추가해보자.
//b에 소유권을 이전한다. 이동 생성자로 사용가능 (복사는 안됨)
std::unique_ptr<A> b = std::move(a);
std::cout << "b : ";
b->some();
std::cout << "\n\n";
//a를 b에 강제로 이동시킨다. b가 new A 로 생성된 객체의 소유권을 갖게 되고 a는 아무것도 가리키지 않는다.
//이제 기존의 unique_ptr인 a는 접근하지 말자
위 코드를 f() 함수의 다음번에 추가하게 되면 아래의 출력을 얻을 수 있다.
move 함수를 통해 a가 가지고 있던 소유권을 그대로 b에 강제 이동 시킨다. 중요한점은 기존의 소유하고 있던 a를 접근해서는 안된다. a가 가리키고 있던 주소값은 nullptr이다.
이번에는 함수의 인자로 전달해보자. 함수의 인자로 전달할때는 원래의 포인터 주소값만을 전달하면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
void f2(A* pointer) { pointer->dosome(4); }
int main() {
std::unique_ptr<A> a(new A());
f2(a.get());
//유일하게 소유한 객체를 지키기위해 함수 인자로는 원래의 포인터 주소값만을 전달한다.
//get : 실제 객체의 주소값 리턴
std::cout << "\n\n";
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
|
위와 같이 unique_ptr 의 get() 함수를 호출하면, 실제 객체의 주소값을 반환한다. 이 경우 f2() 함수가 포인터를 받고 있으며, 함수 내부에서 객체에 접근할 수 있는 권한을 준다.
코드의 실행결과는 아래와 같다.
이제 마지막으로 Class B의 객체를 make_unique<>() 써서 간단하게 생성해보자
auto p = std::make_unique<B>(3, 5);
p->print();
//템플릿 인자로 전달된 클래스의 생성자에 인자들에 직접 완벽한 전달을 수행
std::cout << "\n\n";
위 코드를 실행하면 된다. make_unique 함수는 템플릿 인자로 전달된 클래스의 생성자 인자들에 완벽한 전달 을 수행한다.
위에서 작성했던 std::unique_ptr<A> a(new A()); 이와 같은 코드가 필요없다.
정리하면,
unique_ptr 은 어떤 객체의 유일한 소유권을 나타내는 포인터 이며, unique_ptr 가 소멸될 때, 가리키던 객체도 소멸된다.
만약에 다른 함수에서 unique_ptr이 소유한 객체에 일시적으로 접근하고 싶다면, get() 함수를 통해 해당 객체의 포인터를 전달하면 된다.
만약에 소유권을 이동하고자 한다면, unique_ptr 를 move 함수를 사용 하면 된다.
Reference
'C , C++, C#' 카테고리의 다른 글
[C/C++] weak_ptr (0) | 2020.01.09 |
---|---|
[C/C++] Shared_ptr (0) | 2020.01.06 |
[C/C++] iterator (반복자) (0) | 2019.12.11 |
[C/C++] C++ 17 문법 unpacking(언패킹), 구조체 바인딩 (0) | 2019.12.09 |
[C/C++] #if #elif #endif 조건부 컴파일 (0) | 2019.12.04 |