헤더파일의 정의


어떠한 자료구조이건 간에 '자료구조의 구현''구현된 자료구조의 활용' 은 완전히 구분되도록 ADT를 정의해야 함을 기억해야 한다.


배열을 이용하는 방법.


리스트에 다양한 종류의 데이터를 저장할 수 있게 하기 위한 typedef 선언이 존재한다.


typedef int LData;                    // 리스트에 int형 데이터의 저장을 위한 선언


typedef ArrayList List;            // List는 배열 기반 리스트이다.

이렇듯 ArrayList에 List라는 이름을 별도로 부여한 것이 당장에는 큰 의미가 없어 보인다. 하지만 ArrayList라는 이름에도 typedef 선언을 해 놓으면, 다음과 같이 List에 다른 이름을 부여하는 것만으로도 사용하는 리스트의 종류를 바꿀 수 있다.


typedef LinkedList List;    // List는 연결 기반 리스트이다.

그래서 main 함수에서도 ArrayList가 아닌 List라는 이름을 이용하여 예제를 작성한 것이다.




함수의 정의


curPosition 변수는 저장된 값을 통해서 LFirst 함수와 LNext 함수가 참조해야 할 배열의 위치를 알려주는 변수이다. 그래서 curPosition은 0이 아닌 -1로 초기화한 것이며, 여기에는 아직 데이터의 차모가 진행되지 않았다는 의미가 담겨있다.


"어떠한 라이브러리를 사용하건 저장된 데이터의 조회를 위한, LFirst 함수의 호출과 같은 별도의 과정은 거치기 마련이다."



Posted by scii
:

리스트라는 자료구조는 구현방법에 따라서 다음과 같이 크게 두 가지로 나뉜다.

- 순차 리스트                        배열을 기반으로 구현된 리스트

- 연결 리스트                        메모리의 동적할당을 기반으로 구현된 리스트


하지만 이는 리스트의 구현방법의 차이에서 비롯된 것이기 때문에 이 둘의 ADT가 동일하다고 해서 문제 될 것은 없다. 물론 각각의 특성적 차이 때문에 ADT에 차이를 두기도 한다. 



리스트의 ADT 정의를 위해서 리스트 자료구조의 가장 기본적이고도 중요한 특성

1. 리스트 자료구조는 데이터를 나란히 저장한다.

2. 리스트 자료구조는 중복된 데이터의 저장을 막지 않는다.


자료구조 중에서는 중복된 데이터의 저장을 허용하지 않는 경우도 있다. 하지만 리스트는 이를 허용한다. 

즉, 리스트는 수학적으로 중복을 허용하지 않는 '집합' 과는 다르다. 그리고 이것이 리스트 ADT를 정의하는데 있어서 고려해야 할 유일한 요소이다. 



리스트 자료구조의 ADT

void ListInit(List * plist);        //C++의 생성자 역할

- 초기화할 리스트의 주소 값을 인자로 전달.

- 리스트 생성 후 제일 먼저 호루되어야 하는 함수.


void LInsert(List * plist, LData data);

- 리스트에 데이터를 저장한다. 매개변수 data에 전달된 값을 저장한다.


int LFirst(List * plist, LData * pdata);

- 첫 번째 데이터가 pdata가 가리키는 메모리에 저장된다. 

- 데이터의 참조를 위한 초기화가 진행된다.

- 참조 성공 시 TRUE(1), 실패 시 FALSE(0) 반환.


int LNext(List * plist, LData * pdata);

- 참조된 데이터의 다음 데이터가 pdata가 가리키는 메모리에 저장된다.

- 순차적인 참조를 위해서 반복 호출이 가능하다.

- 참조를 새로 시작하려면 먼저 LFirst 함수를 호출해야 한다.

- 참조 성공 시 TRUE(1), 실패 시 FALSE(0) 반환.


LData LRemove(List * plist);

- LFirst 또는 LNext 함수의 마지막 반환 데이터를 삭제한다. 

- 삭제된 데이터는 반환된다.

- 마지막 반환 데이터를 삭제하므로 연이은 반복 호출을 허용하지 않는다.


int LCount(List * plist);

- 리스트에 저장되어 있는 데이터의 수를 반환한다.


사실 위의 정보만 가지고는 리스트의 활용방법을 정확히 이해하기는 힘들다 이를 위해서는 헤더파일과 위의 함수들을 호출하는 main함수를 보아야 한다. 

그러나 위의 정보만 가지고도 리스트 자료구조가 제공하는 기능을 어느 정도 예측할 수 있어야 한다.      



모든 자료구조는 내부적으로 다양한 정보를 담게 된다. 그저 데이터만 담는 게 아니라 그 데이터를 효율적으로 저장 및 참조하기 위한 정보들도 담기기 마련이다. 따라서 이와 관련된 변수들의 초기화가 서너행되어야 하며 이를 담담하는 함수가 ListInit이다.


LFirst 함수를 호출하도록 ADT를 디자인한 이유는 무엇일까?

- 이에 대한 해답은 "LNext 함수를 호출할 때마다 다음에 저장된 데이터를 얻을 수 있다." 라는 사살에서 찾을 수 있다. 

이것이 가능한 이유는  리스트 내에서 '데이터의 참조위치' 를 기록하기 때문이다. 따라서 처음부터 참조를 새롭게 시작하기 위해서는 바로 이 정보를 초기화해야 한다. 그리고 이를 목적으로 LFirst 함수의 호출을 요구하는 것이다.






※ 자료구조를 공부하다보니 파이썬의 자료형들을 더욱 깊이 알게 되는구나!!

파이썬의 자료형 중 리스트를 다룰때마다 대충 이런식으로 되어있구나 하고 생각했었는데,

C 자료구조를 공부하니 이런식의 ADT가 되어있다는 것을 확실히 알았다.


Posted by scii
:

객체 배열


: 객체 배열 및 객체 포인터 배열은 C언어의 구조체 배열과 구조체 포이터 배열과 유사하다.


객체 기반의 배열은 다음의 형태로 선언한다(Simple이 클래스 이름).

Simple arr[10];


이를 동적으로 할당하는 경우에는 다음의 형태로 선언한다.

Simple * ptrArr = new Simple[10];


이러한 형태로 배열을 선언하면, 열 개의 Simple 객체가 모여서 배열을 구성하는 형태가 된다. 이렇듯 구조체 배열의 선언과 차이가 없다.

하지만 배열을 선언하는 경우에도 생성자는 호출이 된다. 단, 배열의 선언과정에서는 호출할 생성자를 별도로 명시하지 못한다(생성자에 인자를 전달하지 못한다). 즉, 위의 형태로 배열이 생성되려면 다음 형태의 생성자가 반드시 정의되어 있어야 한다.

Simple(void) {...}


그리고 배열 선언 이후에 각각의 요소를 원하는 값으로 초기화시키길 원한다면, 일일이 초기화의 과정을 별도로 거쳐야 한다.



객체 배열의 예제

#include <iostream>

#include <cstring>

using namespace std;


class Person

{

private:

char * name;

int age;


public:

Person(char* myname, int myage)             //생성자

:age(myage)

{

int len = strlen(myname);

name = new char[len];

strcpy(name, myname);

}

Person() //생성자. 배열 생성시 필요한 생성자를 추가하였다.

{

name = NULL;

age = 0;

cout<<"called Person()"<<endl;

}

void SetPersonInfo(char * myname, int myage)

{

name = myname;

age = myage;

}

void ShowPersonInfo() const

{

cout<<"이름: "<<name<<", "<<"나이: "<<age<<endl;

}

~Person() //소멸자

{

delete []name;

cout<<"called Destructor!"<<endl;

}

};


int main(void)

{

Person pArr[3];             //class Person 3개를 가지고있는 배열.

char nameStr[100];

char * strPtr;

int age;

int len;


for(int i=0; i<3; i++)

{

cout<<"이름: ";

cin>>nameStr;

cout<<"나이: ";

cin>>age;

len = strlen(nameStr)+1;

strPtr = new char[len];

strcpy(strPtr, nameStr);

pArr[i].SetPersonInfo(strPtr, age);

}


for(int i=0; i<3; i++)

pArr[i].ShowPersonInfo();


return 0;

}


실행결과




객체 배열 생성시 void형 생성자가 호출됨을 확인할 수 있다. 그리고 배열 소멸시에도 그 배열을 구성하는 객체의 소멸자가 호출됨을 확인할 수 있다.


-----------------------------------------------------------------------------------


객체 포인터 배열


: 객체 배열이 객체로 이뤄진 배열이라면, 객체 포인터 배열은 객체의 주소 값 저장이 가능한 포인터 변수로 이뤄진 배열이다.


#include <iostream>

#include <cstring>

using namespace std;


class Person

{

private:

char * name;

int age;


public:

Person(char* myname, int myage)             //생성자

:age(myage)

{

int len = strlen(myname)+1;

name = new char[len];

strcpy(name, myname);

}

void ShowPersonInfo() const

{

cout<<"이름: "<<name<<", "<<"나이: "<<age<<endl;

}

~Person() //소멸자

{

delete []name;

cout<<"called Destructor!"<<endl;

}

};


int main(void)

{

Person* pArr[3];                 //class Person 가리킬 수 있는 객체 포인트 배열.

char nameStr[100];

int age;


for(int i=0; i<3; i++)

{

cout<<"이름: ";

cin>>nameStr;

cout<<"나이: ";

cin>>age;

pArr[i] = new Person(nameStr, age);    //객체를 생성해서, 이 객체의 주소 값을 배열에 저장하고 있다.

}


for(int i=0; i<3; i++)

{

pArr[i]->ShowPersonInfo();

delete pArr[i]; //new연산을 진행하였으니 delete연산 진행.

}


return 0;

}


실행결과




※ 객체를 저장할 때에는 위의 예제에서 보인 두 가지 방법 중 하나를 택해야 한다. 즉, 저장의 대상을 객체로 하느냐, 객체의 주소 값으로 하느냐를 결정해야 한다.


'Programming > C++' 카테고리의 다른 글

복사 생성자(Copy Constructor)  (0) 2012.05.22
this 포인터  (0) 2012.05.21
생성자 & 소멸자를 이용한 예제  (0) 2012.05.21
생성자를 이용한 예제.  (0) 2012.05.21
소멸자(Destructor)  (0) 2012.05.20
Posted by scii
: