언어 자료구조 알고리즘/Escort 자료구조와 STL

[자료구조와 STL] 6.vector에 자료를 차례대로 보관하기

언제나휴일 2016. 4. 18. 10:15
반응형

2. 2 vector에 자료를 차례대로 보관하기

 

 이번에는 vector를 사용해서 차례대로 보관하는 프로그램을 작성해 보기로 합시다.

 

 소재: 학생 관리 프로그램

 요구 사항

   사용자에 의해 메뉴를 선택하여 선택한 기능을 수행하는 것을 반복합니다.

      학생 정보 추가 (학생의 정보는 번호와 이름이 있습니다.)

      학생 정보 삭제

      번호로 학생 정보 검색

      이름으로 학생 정보 검색

      전체 보기

 

 Stu 클래스는 그대로 사용하고 vector를 사용하는 StuManager 부분만 변경해 봅시다.

 

 인덱스 연산자를 이용할 때는 보관할 최대 요소 개수를 입력받아 vector resize 메서드를 이용하여 0으로 모든 요소를 보관하는 초기 작업을 필요했지만 여기서는 필요가 없습니다. Run 메서드는 vector를 사용하는 부분이 아니라 흐름을 제어하는 부분이기 때문에 똑같이 사용해도 됩니다. 메뉴 선택을 하는 SelectMenu도 변경할 필요가 없겠죠.

 

 학생 정보를 추가하는 AddStu 메서드에서는 번호를 입력받아 이미 있는지 확인합니다. vector의 인덱스 연산을 통해 관리할 때는 해당 인덱스에 학생 정보를 참조하여 0인지 아닌지로 보관 여부를 판단하였습니다. 하지만 여기에서는 vector의 시작 위치에서 마지막 위치 중에 원하는 요소가 있는지를 확인해야 합니다. 이를 위해 특정 번호에 해당하는 학생 정보가 보관되었는지를 판단하는 Exist 메서드를 추가하겠습니다.

 

 Exist 메서드에서는 반복자를 이용하여 보관된 모든 요소를 차례대로 비교해 나가야 합니다. 이를 위해 vector에는 보관된 첫 위치를 얻어올 수 있는 begin 메서드를 제공합니다. 그리고 마지막 보관된 다음 위치(이번에 보관할 위치라고 생각할 수 있겠죠.)를 얻어오는 end 메서드를 제공합니다. begin 메서드와 end 메서드는 iterator를 반환하며 증감 연산자로 다음 위치로 이동하고 간접 연산을 통해 보관된 요소를 참조할 수 있습니다.

 

bool StuManager::Exist(int num)

{

    StuIter seek = base.begin();

    StuIter end = base.end();

    Stu *stu=0;

    for(    ; seek != end; ++seek) //반복자는 비교 연산, 증감 연산이 가능

    {

        stu = *seek; //iterator의 간접 연산의 결과는 보관한 요소(학생 위치 정보)

        if( stu->GetNum() == num)

        {

            return true;

        }

    }

    return false;

}


 AddStu 메서드에서는 사용자가 입력한 번호의 학생 정보가 없을 때 Stu 개체를 생성하여 vector에 차례대로 보관합니다. vector에 차례대로 보관할 때는 push_back 메서드를 사용합니다. vector에서는 보관할 저장소의 용량이 부족하면 내부에서 자동으로 늘려줍니다. 이러한 이유때문에 vector에 차례대로 자료를 보관할 때 저장소의 용량에 대해 크게 신경 쓰지 않아도 됩니다.

 

int num = 0;

cout<<"추가할 학생 번호를 입력하세요."<<endl;

num = ehglobal::getnum();

if(Exist(num))

{

    cout<<"이미 보관된 학생이 있습니다."<<endl;

    return;

}

string name = "";

cout<<"이름을 입력하세요"<<endl;

name = ehglobal::getstr();

//차례대로 보관할 때 맨 뒤에 보관하면 되므로 push_bakc을 호출함

base.push_back(new Stu(num,name));


vector가 꽉 찼을 때 push_back 메서드를 호출하기 전 후 모습

[그림 4] vector가 꽉 찼을 때 push_back 메서드를 호출하기 전 후 모습

 

 RemoveStu 메서드에서는 사용자에게 삭제할 학생 번호를 입력받아 보관된 위치를 찾아야 할 것입니다. 이를 위해 특정 번호에 해당하는 학생이 보관된 위치를 찾는 작업이 필요한 데 함수 개체와 find_if 알고리즘을 사용해 볼게요.

 

 find_if 함수는 STL algorithm에서 제공하고 있습니다. 입력 인자로는 검색할 구간의 시작 위치와 마지막 위치와 요소를 입력 인자로 받습니다. 그리고 보관된 자료를 입력 인자로 받아 조건을 판단할 수 있는 코드를 세 번째 입력 인자로 받습니다. find_if에서 하는 일은 입력받은 구간 사이에 보관된 자료들을 세 번째 입력 인자로 적용했을 때 처음으로 참이 되는 위치를 반환합니다.

 

 RemoveStu에서는 사용자가 입력한 번호에 해당하는 학생이 보관된 위치를 찾아야 합니다. 입력 인자가 보관된 형식인 Stu *인 함수에서 사용자가 입력한 번호와 같은지를 판별하기 위해 함수 개체가 필요합니다. 멤버 필드에 학생 번호가 있고 Stu *가 입력 매개 변수인 함수 호출 연산자를 중복 정의하여 멤버 필드에 보관된 학생 번호와 입력 인자로 들어온 학생의 번호를 비교한 결과를 반환하면 될 것입니다. 입력한 구간 내에 원하는 조건이 참인 요소가 없으면 구간의 끝이 반환되니 주의하세요.

 

 

//학생의 번호와 멤버 변수 num이 같으면 참을 반환하는 함수 개체

CompareByNum sbn(num);

//구간에 보관된 개체를 함수 개체에 적용했을 때 처음으로 참인 위치 반환

StuIter seek = find_if(base.begin(),base.end(),sbn);

if(seek== base.end())//참인 곳이 없을 때

{

    cout<<num<<"번 학생 자료는 보관되지 않았습니다."<<endl;

    return;

}

 

//학생의 번호와 멤버 변수 num이 같으면 참을 반환하는 함수 개체 클래스

class CompareByNum

{

    int num;

public:

    CompareByNum(int num)

    {

        this->num = num;

    }  

    //학생의 번호와 멤버 변수 num이 같으면 참을 반환

    bool operator()(Stu *stu) //함수 호출 연산자 중복 정의

    {

        return(stu && stu->GetNum() == num);

    }

};

 

 그리고 조건이 맞으면 erase 메서드를 이용하여 vector에서 제거하세요. 여기에서는 보관된 학생 개체를 소멸해야 하므로 소멸한 후에 erase 메서드를 호출해야 합니다. vectorerase 메서드를 호출하면 삭제할 위치 뒤에 있는 요소들은 모두 한 칸씩 앞으로 이동하게 됩니다.

 

//iterator의 간접 연산을 통해 보관된 학생 정보를 얻어옮

Stu *stu = *seek;

delete stu;

base.erase(seek); //보관된 요소 삭제


vector에 erase 메서드 호출 전 후

[그림 5] vector erase 메서드 호출 전 후

 

 참고로, AddStu에서 입력한 번호의 학생 데이터가 이미 있는지를 확인을 할 때도 RemoveStu 메서드처럼 함수 개체와 find_if를 사용해도 됩니다. 여기에서는 두 가지 방법을 모두 보여 드리기 위해 다르게 사용하였습니다.

 

 번호로 검색하는 SearchStuByNum 메서드는 RemoveStu처럼 사용자가 입력한 번호에 해당하는 학생 개체를 찾습니다. 단지, 찾은 위치의 학생 정보를 출력하는 것만 다릅니다.

 

//학생의 번호와 멤버 변수 num이 같으면 참을 반환하는 함수 개체

CompareByNum sbn(num);

//구간에 보관된 개체를 함수 개체에 적용했을 때 처음으로 참인 위치 반환

StuIter seek = find_if(base.begin(),base.end(),sbn);

 

if(seek== base.end())//참인 위치가 없을 때

{

    cout<<name<<"번 학생 자료는 보관되지 않았습니다."<<endl;

    return;

}

 

Stu *stu = *seek; //iterator의 간접 연산으로 보관된 요소를 얻어올 수 있음

cout<<stu<<endl;

 

 이름으로 검색하는 SearchStuByName 메서드에서는 비교하는 함수 개체만 다릅니다.

 

//학생 이름과 멤버 변수 name을 비교하여 같으면 참을 반환하는 함수 개체 클래스

class CompareByName

{

    string name;

public:

    CompareByName(string name)

    {

        this->name = name;

    }

    //함수 호출 연산자 중복 정의

    //학생 이름과 멤버 변수 name이 같으면 참을 반환하는 메서드

    bool operator()(Stu *stu)

    {

        return (stu && stu->GetName() == name);

    }

};

 

 전체 학생 정보 출력은 vector의 시작 위치에서 끝 위치를 만날 때까지 보관된 학생 정보를 출력하면 되겠죠.

 

StuIter seek = base.begin();

StuIter end = base.end();

Stu *stu = 0;

 

//iterator는 비교 연산과 증감 연산을 제공, ++연산을 통해 다음 위치로 이동

for ( ;seek != end; ++seek)

{

    //간접 연산자로 보관된 요소를 얻어올 수 있음

    stu = *seek;

    cout<<stu<<endl;

}

 

 그리고 StuManager가 소멸할 때 자신이 생성한 모든 학생 개체를 소멸하는 것을 잊지 맙시다.

 

StuIter seek = base.begin();

StuIter end = base.end();

Stu *stu = 0;

for ( ;seek != end; ++seek)

{

    stu = *seek;

    delete stu;

}

반응형