U E D R , A S I H C RSS

STL/vector/Cook Book

작성자 : ["Lovelyboy^_^"]

벡터를 사용해보기 위한 기본 셋팅(앞으로 편의상 반말로 합니다.)

~cpp 
#include <iostream>
#include <vector>
using namespace std;

int main()
{
	return 0;
}

  • 몇 번 써본결과 vector를 가장 자주 쓰게 된다. vector만 배워 놓으면 list나 deque같은것은 똑같이 쓸수 있다. vector를 쓰기 위한 vector 헤더를 포함시켜줘야한다. STL을 쓸라면 #include <iostream.h> 이렇게 쓰면 귀찮다. 나중에 std::cout, std:vector 이런 삽질을 해줘야 한다. 이렇게 하기 싫으면 걍 쓰던대로 using namespace std 이거 써주자.

int형 배열을 int형 벡터에 복사해 보자.

~cpp 
#include <iostream>
#include <vector>
using namespace std;

typedef vector<int>::iterator VIIT;	// Object형이라면 typedef vector<Object>::iterator VOIT;

int main()
{
	int ar[10] = {45,12,76,43,75,32,85,32,19,98};	// Object형이라면 Object ar[10]={...};

	vector<int> v(&ar[0], &ar[10]);		
		// Object형이라면 vector<Object> v(...);
		// ar배열의 0번 원소부터 9번원소까지를
		// v벡터에다가 복사 [0,10) 처음엔 폐구간
		// 마지막엔 개구간이라는거 명심!

	for(VIIT it = v.begin() ; it != v.end(); ++it)	// 제대로 복사됐나 결과 보기
		cout << *it << endl;

	return 0;
}

  • typedef으로 시작하는 부분부터 보자. 일단 반복자라는 개념을 알아야 되는데, 사실은 나도 잘 모른다.; 처음 배울땐 그냥 일종의 포인터라는 개념으로 보면 된다. vector<int>::iterator 하면 int형 vector에 저장되어 있는 값을 순회하기 위한 반복자이다. 비슷하게 vector<Object>>::iterator 하면 Object형 vector에 저장되어 있는 값을 순회하기 위한 반복자겠지 뭐; 간단하게 줄여쓸라고 typedef해주는 것이다. 하기 싫으면 안해줘도 된다.--;
  • 다음엔 vector<int> v~~ 이부분을 보자. vector<T> 에는 생성자가 여럿 있다. 그 중의 하나로, 배열을 복사하는 생성자를 써보자. 그냥 쓰는법만 보자. 단순히 배열 복사하는 거다. C++ 공부했다면 성안당 10장인가 11장에 복사 생성자라고 나올것이다. 그거다.; 그냥 2번 원소에서 5번원소까지 복사하고 싶다. 하면 vector<int> v(&ar2, &ar6) 이렇게 하면 되겠지?(어째 좀 거만해 보인다.;) 마지막은 개구간이라는걸 명심하기 바란다.
  • for 부분을 보면 앞에서 typedef 해준 VIIT 형으로 순회하고 있는것을 볼수 있다. vector<T>의 멤버에는 열라 많은 멤버함수가 있다. 그중에 begin() 은 맨 처음 위치를 가르키는 반복자를 리턴해준다. 당연히 end()는 맨 끝 위치를 가르키는 반복자를 리턴해주는 거라고 생각하겠지만 아니다.--; 정확하게는 '맨 끝위치를 가르키는 부분에서 한 칸 더간 반복자를 리턴'해주는 거다. 왜 그렇게 만들었는지는 나한테 묻지 말라. 아까 반복자는 포인터라고 생각하라 했다. 역시 그 포인터가 가르키는 값을 보려면 당연히 앞에 * 을 붙여야겠지.

벡터로 동적 배열 쓰기

  • 아마 초보자때 누구나 하는 실수가 있을 것이다. 본인도 그랬다.--;
  • 예제로 숫자를 몇개 입력받나 갯수를 입력받은 만큼, 그만큼 루프를 돌려서 숫자를 입력받는 걸 보자.

~cpp 
#include <iostream>
using namespace std;

int main()
{
	int num;
	cin >> num;

	int ar[num];

	for(int i = 0 ; i < num ; ++i)
	{
		cout << i+1 << "번ㅤㅉㅒㅤ" << endl;	
		cin >> ar[i];
	}
	return 0;
}
  • 자 어디가 틀렸을까? 공부 좀 했으면 누구나 알수 있는 삽질이지만, c++ 배운지 몇주밖에 안되었었던 나로서는 저게 왜 틀렸는지 알수가 없었다.
  • 원인은 그거다. 정적 배열은 컴파일 시에 크기를 잡는다. 입력받는 부분은 컴파일 끝나고 실행할때 크기를 입력받는다. 컴파일러는 당연히 크기를 알수 없으니 에러가 나게된다. 고쳐 보자.

~cpp 
#include <iostream>
using namespace std;

int main()
{
	int num;
	cin >> num;

	int* ar = new int[num];

	for(int i = 0 ; i < num ; ++i)
	{
		cout << i+1 << "번ㅤㅉㅒㅤ : ";	
		cin >> ar[i];
	}
	
	delete [] ar;

	return 0;
}
  • 우리가 여태까지 배운 거만 써보면 이렇게 고칠수 있다. 그 유명-_-한 동적배열이다.--; 아.. delete [] 저거 보기 싫지 않은가? c와 c++의 고질적인 문제점이 바로 저거다. 메모리 관리를 프로그래머가 해줘야 한다는거.. 자바 같은건 지가 알아서 delete 해주지만.. c나 c++에서 delete 안해주면.. X되는 꼴을 볼수 있다. (본인이 한번 경험해 봤다.) 그래서 잘 디자인된 클래스는 클래스 내에서 알아서 없애줘야 한다. 바로 vector를 쓰면 저 짓을 안해줘도 된다. 또 고쳐보자.

~cpp 
#include <iostream>
#include <vector>
using namespace std;

int main()
{
	int num;
	cin >> num;

	vector<int> ar(num);

	for(int i = 0 ; i < ar.size() ; ++i)
	{
		cout << i+1 << "번ㅤㅉㅒㅤ : ";	
		cin >> ar[i];
	}

	return 0;
}

  • vector<int>... 부분을 보면 또 다른 생성자가 보인다. 인자로 숫자 하나를 받는다. 그 만큼 동적 할당 해준다는 뜻이다. delete? 그딴거 안해줘도 된다. 프로그램 끝나면서 int형 벡터 ar이 소멸되면서 알아서 없애준다.
  • 또 하나 살펴볼게 있다. 아까 예제에서는 반복자로 벡터 내부를 순회했었다. 하지만 벡터는 임의접근을 허용한다. 배열처럼 ar4 이런식으로 쓸수 있단 말이다. 편한대로 써주자.

벡터로 2차원 동적 배열 쓰기

  • 데블스 캠프때 유명해진 2차원 동적 배열 쓰기다.
  • 파스칼 삼각형 숙제할때 데브피아에서 쓰는법 배워서 열심히 쓰다가 데블스 캠프때 재동이가 퍼뜨렸다. 1학기 내내 편해서 좋다고 써댔는데.. 지금 보면 내가 저걸 왜 쓰고 있었을까 하는 생각이 든다.
  • vector로 간단히 해결이 가능하다. See also RandomWalk2/Vector로2차원동적배열만들기
  • resize() 메소드는 벡터의 크기를 다시 잡아주는 역할을 한다. 잘 쓰자

Pointer를 저장하는 Container

  • container 에 값이 저장될때는 복사 과정이 수행된다. 그래서 값이 간단한 int, double형 에 대한 containter는 그렇게 문제가 되지 않된다.
  • 덩치가 큰 자료형을 container에 저장할때 마다 복사가 수행된다면, 많은 낭비가 발생한다.
    • 여기서 잡담 하나. 객체를 parameter로 넘길때도 복사가 수행되지 않는 참조를 사용하자.
      ~cpp 
      Func(const Obj& obj) 
      
  • 그래서 pointer만을 이용해서 저장하자. 하지만 쓸모 없는 객체는 우리가 지워줘야 한다. 포인터를 넣어줄때에는, 컨테이너가 소멸될때는 포인터들은 지워지겠지만. 그 포인터들이 가르키는 값들은 지워지지 않기 때문이다.
  • 다음 예제는 pointer로 자료를 넣고, 지우는 예제이다.
    ~cpp 
     #include <iostream> 
    #include <string> 
    #include <vector> 
    using namespace std; 
    
    class Obj 
    { 
    private: 
    	int a; 
    	string c; 
    public: 
    	Obj(int n, const string& str) : a(n), c(str) {} 
    	void showMember() 
    	{ 
    		cout << a << " " 
    			<< c << endl; 
    	} 
    };
    typedef vector<Obj*>::iterator VOIT; 
    
    int main() 
    { 
    	vector<Obj*> v; // 포인터를 저장하는 vector
    	
    	// 벡터에 Obj객체들의 포인터를 넣는다. 
    	v.push_back(new Obj(1, "길동")); 
    	v.push_back(new Obj(3, "허준")); 
    	v.push_back(new Obj(5, "제마")); 
    	
    	// 순회하면서 showMember 메소드 호출하고
    	for(VOIT it = v.begin() ; it != v.end() ; ++it)
    		(*it)->showMember();
    	
    	// 순회하면서 가리키는 값들을 지운다.
    	for(it = v.begin() ; it != v.end() ; ++it)
    		delete *it;
             v.clear();
    	return 0; 
    } 
    

하고 싶은 말

  • 흠. 별루 어려운건 없겠지
  • 이게 제가 알고 있는 STL에 대한 겁니다. 나머지나 더 자세히 알고 싶으신분은 책을 보시든지..; MSDN을 참고하세요. 근데 이정도만 알아도.. 왠만한건 다 합니다. C++만든 그 이름 어려운 사람 BJar... Str...(;) 이 사람이 쓰고 싶은 기능만 쓰랬거든요--; 자바가 쓰기는 쉬워도 역시 난 c++이 좋다.
  • 노파심에서 말하는 건데.. 함수로 객체를 넘길때는 꼭 참조! 참조! 입니다. 값이 안 바뀌면 꼭 const 써주시구여. 참조 안 쓰고 값 쓰면 어떻게 되는지 이펙티브 C++에 잘 나와 있습니다.(책 선전 절대 아님) 복사 생성자를 10번 넘게 호출한다는 걸로 기억함.
  • C++에서는 구조체에서도 생성자가 되더군요.--;
    • 구조체에서 함수역시 가능하고, constructor, destructor역시 가능합니다. 다만 class와 차이점은 상속이 안되고, 내부 필드들이 public으로 되어 있습니다. 상속이 안되서 파생하는 분제들에 관해서는 학교 C++교제 상속 부분과 virtual 함수 관련 부분의 동적 바인딩 부분을 참고 하시기 바람니다.--상민
  • 글구.. 요것도 별거 아닌 팁이긴 한데.. i++과 ++i의 차이점은 다 아시죠? 근데 카운트하거나 루프를 돌때는 차이가 없다고 봐도 좋습니다. ++i가 i++보다 성능이 좋다고 하네요.(see also i++VS++i 이건 뭐..~ 맘대로 하세요^^
  • 02 여러분 c++공부 열심히 해요~
----
STL/vector
Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2021-02-07 05:27:58
Processing time 0.0416 sec