삽질의 현장/- C++

#018_시(c)시(c)해서 C++?!_STL_Vector 예제

shovelman 2015. 7. 15. 03:42

안녕하세요 삽잡이입니다...


이번시간에는 지난시간에 이어 Vector(벡터) 에 대해서 알아보려고 합니다...

그 중에서도 순차 보관, 특정키순, 인덱스 이 세가지에 대해서 말입니다.


간략하게 말씀드리자면

순차보관 같은 경우, 들어오는 순차적으로 저장하는 것입니다.

번호 순 보관 같은 경우에는, 번호 순서대로 저장하는 것입니다.

인덱스 보관 같은 경우에는, 원하는 위치에 저장하는 것입니다.


순차 보관 및 번호 순 보관 같은 경우에는 일일히 하나 하나 비교를 해가며

값을 검색하고 입력하고 보관하고 지우고 하겠지만


인덱스 보관같은 경우에는 벡터를 사용하며

가장 빠르게 검색할 수 있다는 장점을 가지고 있습니다...

일일히 비교할 것이 아니라 원하는 값을 입력하여 바로 검색 보관 삭제 등을 수행할 수 있기 때문이죠...


아무튼...


우선 공통적인 코드입니다.

기본적으로 iostream, vector 헤더를 포함하고 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 
//member.h
 
class Member
{
    string name;
    int num;
public:
    Member(string name, int num)
    {
        this->name = name;
        this->num = num;
    }
    void View()const
    {
        cout << "번호:" << num << " 이름:" << name << endl;
    }
    int GetNum()constreturn num; }
    string GetName()constreturn name; }
};
 
 
cs


기본적으로 번호와 이름의 멤버 변수와 이를 확인할 수 있는 메소드로 구성되어 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 
//App.h
 
typedef vector<Member *>::iterator MIter;
typedef vector<Member *> Members;
class App
{
    Members members;
    static App app;
 
public:
    static App *GetSingleton();
    void Run();
private:
    App();
    ~App();
 
    int SelectMenu();
    void AddMember();
    void RemoveMember();
    void FindMember();
    void ListMember();
 
    MIter FindByNum(int num);
};
 
cs

기본적으로 배열에 값을 저장하는 용도로 사용할 것입니다.

순차적 보관을 위해 구현한 기능들입니다...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
 
 
//App.cpp
 
void App::AddMember()
{
    int num = 0;
    cout << "추가할 회원 번호:";
    cin >> num;
    string name;
    cout << "회원 이름:";
    cin >> name;
    Member* member = new Member(name, num);
    members.push_back(member);
}
void App::RemoveMember()
{
    int num = 0;
    cout << "삭제할 회원 번호:";
    cin >> num;
 
    MIter seek = FindByNum(num);
    if (seek == members.end())
    {
        cout << "존재하지 않습니다." << endl;
        return;
    }
    Member* member = (*seek);
    member->View();
    delete member;
    members.erase(seek);
    cout << "삭제하였습니다." << endl;
}
void App::FindMember()
{
    int num = 0;
    cout << "검색할 회원 번호:";
    cin >> num;
 
    MIter seek = FindByNum(num);
    if (seek == members.end())
    {
        cout << "존재하지 않습니다." << endl;
        return;
    }
    Member* member = (*seek);
    member->View();
}
void App::ListMember()
{
    Member* member = 0;
    MIter seek = members.begin();
    MIter end = members.end();
    for (; seek != end++seek)
    {
        member = (*seek);
        member->View();
    }
}
 
MIter App::FindByNum(int num)
{
    Member* member = 0;
    MIter seek = members.begin();
    MIter end = members.end();
    for (; seek != end++seek)
    {
        member = (*seek);
        if (member->GetNum() == num)
        {
            break;
        }
    }
    return seek;
}
 
 
cs


함수명과 같이

추가, 삭제, 검색, 목록 보기의 기능을 나타내고 있습니다.


이제 기본 순차적 보관방법이 아닌 

특정 키값으로 정렬한 보관 방법의 코드를 소개합니다...


기존의 코드에서 


MIter FindSeat(int num);


메소드가 추가되었습니다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 
 
void App::AddMember()
{
    int num = 0;
    cout << "추가할 회원 번호:";
    num = EHLib::getnum();
    string name;
    cout << "추가할 회원 이름:";
    name = EHLib::getstr();
    Member* member = new Member(name, num);
    MIter seek = FindSeat(num);
    members.insert(seek,member);
}
 
MIter App::FindSeat(int num)
{
    Member* member = 0;
    MIter seek = members.begin();
    MIter end = members.end();
    for (; seek != end++seek)
    {
        member = (*seek);
        if (member->GetNum() >= num)
        {
            break;
        }
    }
    return seek;
}
 
 
cs


주목해야할 것은... 멤버를 추가할 때 새로 추가된 메소드를 사용하는 것입니다.

FindSeat 메소드 같은 경우에

기존의 벡터와 입력한 값을 순차적으로 비교하며 순서대로 값을 저장하고자 시도하고 있습니다.



다음으로는 우리가 원하는 곳에 입력하여 벡터에 보관하고자하는

인덱스 보관 방법에 대해 알아보려고 합니다..


인덱스로 보관할 때에는 Primary Key가 중요합니다... 기본키라고들 하지요...

혹시 주민등록번호 없으신 분 계신가요?

아니면 자신의 주민등록번호가 다른 사람과 동일하신 분은??


이와 같이 모든 멤버들이 반드시 가지고 있으면서 같은 값을 가지지 않는...

즉, 무결성과 유일성을 보장해주는 primary key가

인덱스 보관시 중요한 것입니다....

왜냐, 내가 원하는 곳에 보관하려하지만 무결성과 유일성이 보장되지 않는다면

이를 시행할 수 없기 때문입니다.


우리가 이 포스팅에서 보고 있는 간략한 코드에서는

번호 즉, name 멤버 변수 값을 primary key로 정하여 진행을 해보도록 하겠습니다.


인덱스 보관을 하겠다는 것은 최대값이 주어져야 합니다.

즉, primary 키는 최대값과 최소값을 알고 있어야 하는 필수 조건이 있습니다.

이 범위 안에서 사용자가 정한 곳에 값을 보관하겠다는 취지이죠...

지정석같은 것이지요...


그렇다는것은 데이터의 유무를 파악할 수 있어야 되는 것이죠...

해당 자리에 앉아있는 사람이 있을 수 있으니까요...


이를 위해 초기에 약속된 default 값으로 모든 벡터 멤버 값들을 0으로 초기화를 해 둡니다.

분별하기 위해서요...


인덱스(index) 보관을 할 경우에

기존의 순차적 보관의 단점을 보완할 수 있습니다.

순차적 보관같은 경우 일일히 하나씩 비교를 해가며 판단을 해야하기에

만약 999999999999999999999999999999999 개의 값들을 하나하나씩 비교한다면...

컴퓨터도 싫어하겠군요... 허허...


아무튼... 

index 지정을 통해 원하는 곳을 찔러서 맞출 수 있는... 기존의 단점을 보완할 수 있다 이거죠~!


index 보관을 할 때에는 모든 값이 유효한지 확인해야합니다...

default 값으로 모두 설정되있기 때문에

중간 중간 유효하지 않은 값으로 되어 있으면 곤란하니 확인하는 작업이 필요한 것이지요...


또한 primary key로 사용할 멤버에 대한 정책이 유효한지 확인해야합니다.


아무튼.... 검색 효율이 좋은... 


정말 코드를 확인해보시죠!


추가된 헤더입니다...


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 
 
typedef vector<Member *>::iterator MIter;
typedef vector<Member *> Members;
class App
{
    Members members;
    static App app;
    int max; // 최대 멤버 번호
public:
    static App *GetSingleton();
    void Run();
private:
    App();
    ~App();
 
    int SelectMenu();
    void AddMember();
    void RemoveMember();
    void FindMember();
    void ListMember();
 
    bool AvailNum(int num); //유효한 index인지 확인
};
 
 
cs


index를 지정했는데 최대값을 넘어버렸다면

엉뚱한 쓰레기 값을 찾아 헤매는 것이죠,.. 이를 막기위해 멤버변수를 추가했습니다.

또한 벡터 값 내에 값이 유효한지 확인을 위한 메소드를 또한 추가했습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
 
 
App::App(void)
{
    cout << "최대 멤버 번호: ";
    cin >> max;
    members.resize(max, 0);
}
 
App::~App(void)
{
}
 
void App::AddMember()
{
    int num = 0;
    cout << "추가할 회원 번호:";
    cin >> num;
    if (AvailNum(num) == false)
    {
        cout << "유효하지 않은 번호입니다." << endl;
        return;
    }
    if (members[num - 1])
    {
        cout << "이미 존재하는 번호입니다..." << endl;
        return;
    }
 
    string name;
    cout << "추가할 회원 이름:";
    cin >> name;
    Member* member = new Member(name, num);
    // 지정한 index에 default 값을 입력한 값으로 변경
    members[num - 1= member; 
}
 
void App::RemoveMember()
{
    int num = 0;
    cout << "삭제할 회원 번호:";
    cin >> num;
 
    if (AvailNum(num) == false)
    {
        cout << "유효하지 않은 번호입니다.." << endl;
        return;
    }
    if (members[num - 1== 0// default 값이라면
    {
        cout << "없는 번호입니다." << endl;
        return;
    }
 
    Member* member = members[num-1];
    member->View();
    delete member;
    members[num - 1= 0//삭제가 아닌 default값으로 변경
    cout << "삭제하였습니다.." << endl;
}
 
void App::FindMember()
{
    int num = 0;
    cout << "검색할 회원 번호:";
    cin >> num;
 
    if (AvailNum(num) == false)
    {
        cout << "유효하지 않은 번호입니다." << endl;
        return;
    }
    if (members[num - 1== 0)
    {
        cout << "없는 번호입니다." << endl;
        return;
    }
    Member* member = members[num-1];
    member->View();
}
 
void App::ListMember()
{
    for (int i = 0; i < max; ++i)
    {
        if (members[i]) //멤버 유효 확인
        {
            members[i]->View();
        }
    }
}
 
bool App::AvailNum(int num)
{
    return (num >= 1) && (num << max);
}
 
 
cs


주석으로 충분히 이해가 가능할 듯하지만...

우선 index라는 값이 유효한지, 

또한 선택한 index에 값이 들어있는지에 대한 유효성 검사를 하는 부분이 추가되었습니다.


처음 생성할때 입력했던 최대값까지의 배열 값들을 default 값으로 초기화를 진행했고,

index의 유효성 검사 후 default 값을 입력한 값으로 변경하는 작업을 추가했습니다.


중요한 것은, index 지정 후 멤버를 삭제할 때에는

값 자체를 삭제하는 것이 아닌 default 값으로 변경하는 것입니다...

지정석이 정해져있는데 지정석에 멤버가 빠진다고 의자를 뽑아버리진 않죠... 푸하하...



이상으로 vector를 사용하여 이것저것 알아봤네요 ㅎㅎ


다음시간에는 vector 내부를 좀 세세하게 들여다볼 수 있지 않을까 합니다...


수고하셨습니다...

이상 삽잡이였습니다!