Dart의 값 복사와 참조 복사 (Deep Copy vs. Shallow Copy)
C++를 주로 하다가 Dart 언어로 넘어오면서 자주 착각한 게 있는데, 바로 Dart는 기본적으로 참조 복사가 일어난다는 것이다.
Dart의 구조
값 복사는 변수에 값을 할당할 때 실제 데이터의 복사본이 생성되는 것을 의미한다.
Dart에서는 기본 자료형(int, double, bool, String 등)에서 값 복사가 일어난다.
반면, 참조 복사(reference copy)는 변수에 값을 할당할 때 데이터의 주소를 복사하는 것을 의미하며, Dart에서는 기본 자료형을 제외한 나머지 객체(Object)에서 주로 발생한다.
영어에서 값 복사는 deep copy, 참조 복사는 shallow copy라고 불린다.
예시 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
void main() {
// 값 복사 예시
int a = 10;
int b = a;
b = 20;
print('a: $a, b: $b'); // a: 10, b: 20
// 참조 복사 예시
List<int> list1 = [1, 2, 3];
List<int> list2 = list1;
list2[0] = 100;
print('list1: $list1, list2: $list2'); // list1: [100, 2, 3], list2: [100, 2, 3]
}
위의 코드에서 int 타입 변수는 값 복사가 일어나기 때문에 b의 값을 변경해도 a에 영향을 주지 않는다. 반면에 List
주의할 점은 단순히 copy뿐만 아니라 함수의 인자로 넘겨줄 때도 참조 복사가 일어난다는 점이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
void changeValues(int num, List<int> list) {
num = 20;
list[0] = 100;
print('Inside function: num = $num, list = $list'); // num = 20, list = [100, 2, 3]
}
void main() {
int a = 10;
List<int> list1 = [1, 2, 3];
changeValues(a, list1);
print('Outside function: a = $a, list1 = $list1'); // a = 10, list1 = [100, 2, 3]
}
따라서 dart로 개발할 때는 parameter로 전달한 변수가 의도치 않게 수정되지 않도록 주의가 필요하다.
만약 값복사를 하고 싶다면?
참조 복사가 아닌 값복사(deep copy)를 원할 경우에는 수동으로 객체의 모든 필드를 복사해야 한다.
만약 직접 만든 class의 경우, class에 clone 메서드를 추가하는 것이 좋다. (아니면 매번 새 오브젝트를 만들어서 일일이 복사하거나)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Person {
String name;
int age;
Person(this.name, this.age);
// deep copy를 위한 메서드
Person clone() {
return Person(this.name, this.age);
}
}
void changePerson(Person person) {
person.name = 'John';
print('Inside function: ${person.name}, ${person.age}'); // John, 30
}
void main() {
Person p = Person('Alice', 30);
Person pCopy = p.clone();
changePerson(pCopy);
print('Outside function: ${p.name}, ${p.age}'); // Alice, 30
}
List, Sets, Maps같은 Built-in types에서는 from() 메서드를 지원하여 쉽게 값복사를 할 수 있다.
1
2
3
4
5
6
7
8
void main() {
List<int> originalList = [1, 2, 3];
List<int> copiedList = List.from(originalList);
copiedList[0] = 100;
print('Original List: $originalList'); // [1, 2, 3]
print('Copied List: $copiedList'); // [100, 2, 3]
}