1. C++의 포인터와 Rust의 소유권
C++의 포인터와 Rust의 소유권은 상당히 비슷한 개념입니다. 다만 C++의 std::move와 Rust의 소유권은 비슷한 아이디어를 사용하지만, 언어 차원에서 보장하는 수준이 다릅니다.
1.1 먼저 '소유권'이란 무엇일까요?
쉽게 말하면
"이 메모리를 누가 책임지고 관리할 것인가?" 입니다.
예를 들어 문자열을 하나 만들었다고 해보겠습니다.
std::string a = "ABC";
메모리는 대략 이렇게 생겼다고 생각하면 됩니다.
스택 힙
+---------+ +---------+
| a | ---------> | "ABC" |
+---------+ +---------+
여기서 a는 힙에 있는 "ABC"를 소유(owner) 하고 있습니다.
즉,
- 메모리를 사용할 권리
- 메모리를 해제할 책임
을 모두 a가 가지고 있습니다.
1.2 복사를 하면?
std::string b = a;
이 경우는 소유권을 넘기는 것이 아닙니다.
새로운 문자열을 하나 더 만듭니다.
스택 힙
a ---------> "ABC"
b ---------> "ABC"
힙에 문자열이 두 개 존재합니다.
따라서
- a가 첫 번째 문자열 관리
- b가 두 번째 문자열 관리
각자 자기 메모리를 관리합니다.
1.3 move를 하면?
std::string b = std::move(a);
이번에는 새로운 문자열을 복사하지 않습니다.
포인터만 옮깁니다.
이전
a ---------> "ABC"
이후
a ---------> (비어있음)
b ---------> "ABC"
실제로는 내부 포인터만 이동합니다.
즉,
힙 메모리의 주인이 a에서 b로 바뀌는 것입니다.
그래서 이것을 소유권을 이동(move) 한다고 합니다.
1.4 왜 a를 비워둘까요?
만약 이렇게 된다면
a ---------> "ABC"
b ---------> "ABC"
둘 다 같은 메모리를 소유한다고 생각해보겠습니다.
프로그램이 끝나면
a가 delete
b도 delete
를 수행합니다.
같은 메모리를 두 번 해제하게 됩니다.
이를 Double Free라고 하고 프로그램이 망가집니다.
그래서 move를 하면
a -> 비움
b -> 소유
이렇게 만들어 버립니다.
1.5 Rust와 비교하면?
여기서 Rust를 공부해 보셨다면 바로 떠오르는 개념이 있을 것입니다.
Rust에서는
let s1 = String::from("ABC");
let s2 = s1;
하면
s1 -----> (사용 불가)
s2 -----> "ABC"
가 됩니다.
즉,
소유권이 이동합니다.
이건 C++의
std::string s2 = std::move(s1);
와 거의 같은 그림입니다.
1.6 Rust와의 차이점
여기가 가장 중요한 차이입니다.
Rust
let s1 = String::from("ABC");
let s2 = s1;
println!("{}", s1); // 컴파일 에러
아예 컴파일이 안 됩니다.
언어 자체가
"소유권이 이동했으니 이제 s1은 절대 쓰면 안 된다."
라고 강제합니다.
C++
std::string s1 = "ABC";
std::string s2 = std::move(s1);
std::cout << s1;
컴파일은 됩니다.
다만 move된 객체는 '유효하지만 값은 보장되지 않는 상태(valid but unspecified state)' 가 됩니다.
즉
- 빈 문자열일 수도 있고
- 구현에 따라 다른 상태일 수도 있습니다.
따라서 일반적으로는 다시 값을 넣거나 파괴(destroy)하기 전까지는 원래 내용에 의존하지 않는 것이 좋습니다.
1.7 그래서 C++는 Rust보다 덜 엄격합니다.
Rust
move
s1 ❌ 사용 금지
C++
move
s1 ⭕ 사용 가능
하지만 내용은 믿지 마세요.
비유하면
책 한 권이 있다고 생각해보겠습니다.
복사(copy)
철수 📕
영희 📕
책을 한 권 더 인쇄했다.
책이 두 권이므로 서로 영향을 주지 않습니다.
이동(move)
철수 ❌
영희 📕
철수가 책을 영희에게 넘겼습니다.
책은 여전히 한 권뿐입니다.
Rust
책을 넘긴 철수는
" 이제 그 책을 다시 펼쳐볼 수 없습니다."
라고 언어가 막습니다.
C++
책을 넘긴 철수도 여전히 책장을 열려고 시도할 수는 있습니다.
하지만 그 안에 무엇이 들어있는지는 보장되지 않으므로, 원래 내용을 기대해서 사용하면 안 됩니다.
※ 정리
- C++의 std::move와 Rust의 소유권 이동은 기본 아이디어가 매우 비슷합니다. 둘 다 복사하지 않고 자원의 소유권을 다른 객체로 넘긴다는 개념입니다.
- Rust는 소유권 규칙을 언어 차원에서 엄격하게 강제하여 잘못된 사용을 컴파일 시점에 막습니다.
- C++는 std::move를 통해 이동을 표현하지만, 이동된 객체의 사용은 프로그래머의 책임에 맡깁니다. 그래서 성능 면에서는 매우 유연하지만, 올바르게 사용하는 데 더 많은 주의가 필요합니다.
'Fundamental of CS > : : C++' 카테고리의 다른 글
| [OOP] C++ OOP Intro (0) | 2024.07.16 |
|---|---|
| [Resource Move] Return Value Optimization(RVO) (0) | 2024.07.16 |
| [Resource Move] std::move() (0) | 2024.07.16 |
| [Resource Move] L-Value, R-Value 그리고 std::move (0) | 2024.07.16 |
| [Resource Move] C++ Value, Pass by Value / Pointer / Reference (0) | 2024.07.16 |