vector memory

김대익·2022년 3월 15일
0
#include <iostream>
#include <vector>

int main() {
	std::vector<int> nums;
    std::cout << sizeof(nums) << std::endl;
    
    return 0;
}

를 출력해보면 24bytes가 결과로 나온다.
이를 그림으로 그리면

stack에 nums vector 객체가 생기고 힙에는 아무것도 생성되지않은 상태이다.
그러므로 vector 객체가 24bytes인 것인데
이는 첫 8bytes는 힙 위에 있는 array의 시작점 포인터정보로
64bits컴퓨터의 경우 포인터가 8bytes이다.
두번째는 size에 대한 정보 8bytes,
세번째는 capacity에 대한 정보 8bytes이다.

#include <iostream>
#include <vector>

int main() {
	std::vector<int> nums{1,2,3,4,5};
    std::cout << nums.size() << std::endl;
    std::cout << nums.capacity() << std::endl;
    
    nums.emplace_back(6);
    std::cout << nums.size() << std::endl;
    std::cout << nums.capacity() << std::endl;
    return 0;
}

을 출력해보면
5,5
6,6이 아닌

이 출력된다.

왜냐하면 emplace_back시 size는 1개씩 증가하지만 capacity는 확보한 크기를 넘어서면 보통 2배로 증가하기 때문이다.



만약 emplace_back을 하려고하는데 이미 그 메모리부분이 사용 중이라면 새로 배열을 만들어
move가 가능하면 move, 안된다면 copy를 한 뒤에

기존의 array를 해제하고 nums vector 객체의 포인터를 변경시킨다.
이 경우 emplace_back은 O(n)의 time complexity를 가진다.

emplace_back을 O(1)의 time complexity로 유지하고싶다면
reserve()함수로 미리 공간을 확보해두면 된다.


#include <iostream>
#include <string>
#include <vector>

class Cat {
public:
	explicit Cat(std::string name) : mName{std::move(name)} {}
    ~Cat() {
    	std::cout << "~Cat()" << std::endl;
    }
    Cat(const Cat& other): mName(other.mName) {
    	std::cout << "copy constructor" << std::endl;
    }
    Cat(Cat&& other): mName{std::move(other.mName)} {
    	std::cout << "move constructor" << std::endl;
    }
    
private:
	std::string mName;
};

int main() {
	std::vector<Cat> cats;
    cats.emplace_back("Kitty");
    cats.emplace_back("nabi");
}

위 코드를 실행하면

destructor가 3번 실행되는데

힙메모리에 기존 Kitty 객체 생성되고 뒤에 바로 nabi가 생기는게 아니라
공간을 새로 생성하고 Kitty를 복사한 뒤 뒤에 nabi가 생기기 떄문이다.

move가 아닌 copy가 일어난 이유는
class 정의시 exception이 날 수 있기 때문에
컴파일러가 안전하게 copy를 부르는 것이다.

public:
	explicit Cat(std::string name) : mName{std::move(name)} {}
    ~Cat() noexcept{
    	std::cout << "~Cat()" << std::endl;
    }
    Cat(const Cat& other): mName(other.mName) {
    	std::cout << "copy constructor" << std::endl;
    }
    Cat(Cat&& other) noexcept: mName{std::move(other.mName)} {
    	std::cout << "move constructor" << std::endl;
    }

현재 move는 새로운 리소스를 요쳥하지 않으므로 noexcept를 붙여주면 move를 부르게된다.

또는
reserve를 사용하면 복사가 되지않는다.

0개의 댓글