C++

[강의] 12월 24일 수업정리

k-codestudy 2024. 12. 25. 02:30

오늘은 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