배열과 비슷하며, 크기가 동적으로 확장될 수 있는 동적배열 기반의 구조이다.
배열처럼 인덱스로 요소에 접근이 가능하다.
벡터안에 데이터가 꽉 차면, 벡터의 크기를 2배로 늘린 뒤 원래의 데이터 값을 복사하여 넣어주고, 새 값을 추가해야한다
Template T로 벡터 클래스를 구현해 볼 것이다
#include <iostream>
#define SIZE 2
using namespace std;
template <typename T>
class Vector
{
private:
int capacity;
int size;
T* container;
public:
Vector()
{
capacity = 0;
size = 0;
container = nullptr;
}



void Resize(int newSize)
{
// 1. capacity에 새로운 size값을 저장합니다.
capacity = newSize;
// 2. 새로운 포인터 변수를 생성해서 새롭게 만들어진
// 메모리 공간을 가리키도록 합니다. (크기가 capacity인)
T* newContainer = new T[capacity];
// 3. 새로운 메모리 공간의 값을 초기화합니다.
for (int i = 0; i < capacity; i++)
{
newContainer[i] = NULL;
}
// 4. 기존 배열에 있는 값을 복사해서 새로운 배열에 넣어줍니다.
for (int i = 0; i < size; i++)
{
newContainer[i] = container[i];
}
// 5. 기존 배열의 메모리를 해제합니다.
if (container != nullptr)
{
delete[] container;
}
// 6. 기존에 배열을 가리키던 포인터 변수의 값을
// 새로운 배열의 시작 주소로 가리킵니다.
container = newContainer;
}
void PushBack(T data)
{
if (capacity <= 0)
{
Resize(1);
}
else if (size >= capacity)
{
Resize(capacity * 2);
}
container[size++] = data;
}
capacity가 0보다 같거나 작을 경우 -> 벡터에 메모리가 아직 할당되지 않은 상태 -> 메모리를 할당해주어야함

size 가 capacity보다 크거나 같을 경우 -> 할당된 메모리 크기보다 더 큰 양의 원소가 들어갈경우 capacity를 새로 할당해서 만들어 줘야 한다
주로 capacity값을 2배로 할당해준 뒤 기존의 배열 데이터의 값을 새 배열로 복사하여주고 기존의 배열은 해제한다
container 포인터의 값을 새로 할당된 배열로 연결시켜주면 된다
배열에 데이터를 넣어준 뒤 size++하여 이동

void PopBack()
{
if (size <= 0)
{
cout << "Vector is Empty" << endl;
}
else
{
container[--size] = NULL;
}
}

const int& Size()
{
return size;
}
여기서 const 란?
반환하는 size를 상수로 지정하기 위해 사용된다
반환된 size의 값은 상수이기에 값이 변할 수 없다
여기서 &를 사용하는 이유는?
만약에 &(참조)없이 메인함수에서 Size( )함수를 호출 할 경우, size의 값이 복사되어 반환된다 -> 불필요한 복사비용이 발생한다
그러나, &가 붙게되면 여기서는 반환되는 값은 size 변수에 대한 참조이다
이는, 클래스 내부의 size값과 동일한 값을 가지면서 복사비용은 발생하지 않는다
const와 &를 함께 쓰면 읽기 전용이 보장된다
아래의 예시를 보면,
참조(&)가 없을 경우
메인 함수에 들어있는 변수 a의 값은 10
매개변수로 a의 값인 10이 복사 되어서 들어간다
둘다 별도의 메모리 공간을 만들었기 때문에 a와 num은 서로 별개의 것이다
매개변수로 들어간 num의 값은 함수를 거쳐서 1이 된다
하지만, 메인함수 쪽 a에는 연관이 없다
-> 결론: num의 값: 1 a의 값: 10
참조(&)가 있는 경우
메인 함수에서 들어있는 변수 a의 메모리 공간이 있다
변수 a가 복사되어 매개변수로 들어가지 않고, 본래 그대로 매개변수로 들어간다
Function함수를 거치면 매개변수로 받은 값이 1로 변한다
a 와 num은 서로 동일한 것 (같은 값을 가지고 이름만 2개를 쓴다는 뜻)
-> 결론: a의 값: 1

매개변수로 들어오는 값은 내가 접근할 인덱스의 번호를 가리킨다
-> 메인함수에서 클래스의 객체를 만들고 객체를 통해 배열의 인덱스로 접근할 수 있다
내가 원하는 배열 인덱스에 들어있는 값을 반환
operator [ ] 키워드
[ ] 대괄호를 사용하여 객체의 멤버에 접근할 수 있는 메소드
ex) 클래스타입의 객체를 하나 생성하고 그 객체를 통해 배열의 인덱스에 접근한 뒤 값을 얻어낸다
int main()
{
Vector vector;
cout << vector[4];
}
const T& operator [] (const int index)
{
return container[index];
}
~Vector()
{
if (container != nullptr)
{
delete[] container;
}
}
int main()
{
Vector <int> vector;
vector.PushBack(10);
vector.PushBack(20);
vector.PushBack(30);
vector.PopBack();
for (int i = 0; i < vector.Size(); i++)
{
cout << vector[i] << endl;
}
cout << "Vector의 크기 : " << vector.Size();
return 0;
}
capacity와 size를 별도로 관리하기 때문에 메모리가 낭비된다
->capacity는 할당된 전체 메모리 크기이고, size는 현재 들어있는 원소의 수기 때문에 size값을 제외한 capacity의 빈 부분은 남는 메모리로 남게된다
-> 새 메모리를 할당 한 경우, 사용하지 않는 공간이 일시적으로 발생한다
배열 처럼 사용되기 때문에 요소의 삽입과 삭제 시 요소를 이동시켜야한다 -> 시간복잡도가 O(n)
size가 capacity 용량을 초과하게 되면 새 배열에 새 메모리를 할당하고 기존 데이터를 복사해야 하기 때문에 메모리 재할당 비용이 발생한다
벡터는 배열처럼 사용되기 때문에 인덱스를 통해서 원하는 요소에 바로 접근이 가능하다 -> 시간복잡도가 상수시간 O(1)
동적으로 메모리를 할당하기 때문에, 요소를 삽입하거나 제거하는 데 유리하다