오늘은 list, vector에 대한 수업을 들었다
1. list
earse를 사용하는 방법에 대한 것이다.
1.1 erase를 로직으로 해결한 방법 1
- iter를 백업한 iterNext를 사용하여 다음 노드를 미리 가리키게 함.
- 현재 노드를 지운 뒤 iterNext를 다시 iter에 할당.
std::list<int>::iterator iter = listData.begin();
while (iter != listData.end())
{
std::list<int>::iterator iterNext = iter;
iterNext++;
if (*iter > 5)
listData.erase(iter);
iter = iterNext;
}
1.2 erase를 로직으로 해결한 방법 2
- iter++을 후위 증가시켜 erase 호출 전 현재 값을 사본으로 남기고, erase 후 자동으로 다음 노드를 가리키게 함.
std::list<int>::iterator iter = listData.begin();
while (iter != listData.end())
{
if (*iter > 5)
//listData.erase(iter++);
else
iter++;
}
print(listData);
1.3 earse의 기능을 사용한것
- erase의 반환값으로 삭제된 노드의 다음 노드를 가리키는 반복자를 받아 활용.
#include <iostream>
#include <list>
void print(const std::list<int>& list);
int main()
{
std::list<int> listData{};
listData.push_back(1);
listData.push_back(9);
listData.push_back(10);
listData.push_back(4);
listData.push_back(5);
listData.push_back(7);
print(listData);
std::list<int>::iterator iter = listData.begin();
while (iter != listData.end())
{
if (*iter > 5)
iter = listData.erase(iter); // earse의 기능을 이용하여 해결한 것
else
iter++;
}
print(listData);
}
void print(const std::list<int>& list)
{
std::list<int>::const_iterator iter = list.begin();
while (iter != list.end())
{
printf("%d ", *iter);
iter++;
}
printf("\n");
}
2.vector
2.1 Vector 메모리 할당 과정
#include <iostream>
#include <vector>
int main()
{
std::vector<int> vData{};
for (int i = 0; i < 10; i++)
{
vData.push_back(i);
printf("실제 사이즈 : %lld, 사용중인 크기 : %lld\n", vData.capacity(), vData.size());
}
}
push_back으로 요소를 추가할 때 마다 백터의 실제 크기(capacity)와 사용 중인 크기(size)가 출력 되는게 결과를 봐보자
실제 사이즈가 점점 커져가는걸 볼수있다
이유로는 백터는 추가되는 요소를 저장하기 위해 새로운 메모리를 생성하게 되고 기존 데이터 새 메모리로 복사 후 이전 메모리는 해제를 하게 되며 이 과정이 반복되면서 백터의 실제크기는 기존 크기보다 약 1.3배 정도 증가하게 된다.
[ 메모리 생성 -> 복사 -> 해제 ]
이런 과정이 있기에 요소 추가 시 메모리르 자주 재할당하게 되면 비용이 증가하게 되고 특히, 데이터가 많을수록 재할당과 복사 비용이 커지게 된다.
2.2 reserve와 resize의 차이점
2.2.1 reserve
- 메모리 공간만 확보(예약)하지만, 실제 데이터를 생성하지 않음 ( 포인터처럼 메모리를 할당 ).
- 메모리 재할당 횟수를 줄여 성능을 개선.
vData.reserve(10);
2.2.2 resize
- 크기를 변경하고, 필요한 경우 기본값으로 초기화.
- resize한 크기만큼 실제 데이터를 생성.
vData.resize(10);
2.3 reserve와 resize를 활용한 예제
#include <iostream>
#include <vector>
int main()
{
// std::vector<int> vData(10); // 배열을 만든거, 인덱스 접근을 위한 배열이 된것
std::vector<int> vData{};
vData.reserve(20);
vData.resize(10);
for (int i = 0; i < 10; i++)
{
printf("실제 사이즈 : %lld , 사용중인 크기 : %lld\n", vData.capacity(), vData.size());
}
}
reserve
- 역할: 벡터의 메모리 공간만 미리 예약.
- 데이터를 추가하기 전에 충분한 공간을 확보해, 메모리 재할당 횟수를 줄임.
- 실제 데이터는 생성되지 않음.
- ArrayList처럼 사용하고자 할 때 : 크기가 동적으로 늘어나며, 메모리 재할당을 줄이고 싶은 경우
resize
- 역할: 벡터의 크기를 변경하고, 필요한 경우 기본값으로 초기화.
- reserve보다 작거나 같아도 사용 크기를 강제로 설정.
- 실제 데이터를 초기화한 상태로 사용할 수 있음.
- 배열처럼 사용하고자 할 때 : 명확한 크기와 초기값을 필요로 하는 경우.
2.4 주의점
- resize로 크기를 설정해도, 내부 데이터를 초기화만 했을 뿐 값이 채워져 있지 않을 수 있음.
- 크기가 변동될 가능성이 있다면 reserve를 사용해 성능을 최적화할 수 있음.
3. 문제, 백터 3 * 5 이차원 형태의 배열을 할당하고 출력
3.1 내 코드 (값 초기화와 동시에 메모리 할당)
- std::vector 생성 시 크기와 초기값을 지정하여 초기화.
#include <iostream>
#include <vector>
int main()
{
std::vector<std::vector<int>> vData(3,std::vector<int>(5,0));
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf("%d ", vData[i][j]);
}
printf("\n");
}
}
3.2 강사님 코드 (점진적 메모리 할당)
- resize를 사용하여 1차원 벡터를 생성한 뒤, 각 벡터의 크기를 개별적으로 설정.
#include <iostream>
#include <vector>
int main()
{
std::vector<std::vector<int>> vData;
vData.resize(3);
for (int i = 0; i < 3; i++)
{
vData[i].resize(5);
}
for (int i = 0; i < vData.size(); i++)
{
for (int j = 0; j < vData[i].size(); j++)
{
printf("%d ", vData[i][j]);
}
printf("\n");
}
}
'C++' 카테고리의 다른 글
[강의] 12월 27일 수업정리 (0) | 2024.12.27 |
---|---|
[강의] 12월 26일 수업정리 (1) | 2024.12.26 |
[강의] 12월 23일 수업정리 (1) | 2024.12.23 |
[강의] 12월 20일 수업정리 (2) | 2024.12.20 |
[강의] 12월 19일 수업정리 (1) | 2024.12.19 |