Programming/Programming2018. 12. 23. 17:31

1.     컴파일 환경

Visual C++ 2015 (_MSC_VER==1900) 환경에서 디버그 및 컴파일했다.

 

2.     클래스 설계

MyIntVector.h 헤더 파일의 소스코드를 일부씩 가지고 와서 설명하겠다. 크게 멤버변수, 생성자, 멤버함수로 나누어 설명한다. 생성자와 멤버함수의 precondition postcondition을 헤더 파일에 작성하였으나, precondition을 만족시키지 못할 경우의 예외처리를 최대한 하는 방향으로 코드를 작성했다.

 

A.     멤버변수



멤버 변수는 데이터를 담는 int*형 변수 data data의 최대 수용할 수 있는 원소의 개수를 값으로 가지는 capacity, 그리고 저장된 원소의 개수를 값으로 갖는 size로 구성했다.

 

B.      생성자 및 소멸자



생성자는 총 2개로, 첫 번째 생성자는 정수를 전달받아 그 정수를 capacity의 값으로 초기화한다. 이 생성자는 기본값으로 DEFAULT_CAPACITY를 가지므로 아무것도 전달되지 않았을 때는 매크로 상수로 정의된 DEFAULT_CAPACITY의 값으로 capacity 값을 초기화 하게 된다. 현재 설정해놓은 DEFAULT_CAPACITY 32이다.



나머지 한 생성자는 copy constructor이다. 인자로 MyIntVector의 참조자를 받아 그 참조하는 MyIntVector와 완전히 동일한 새로운 객체를 만든다. , 생성되는 객체의 data에 참조한 data deep copy하고 capacity size를 그대로 복사한다.

마지막으로 소멸자는 동적 할당된 data delete해준다.

 

C.      연산자


연산자는 =,+=, [], +, -, *, -(Unary), ==, ()를 재정의했다.


= 연산자이다. 좌항에 있는 MyIntVector 객체에 우항의 MyIntVector deep copy한다. 좌항과 우항에 같은 MyIntVector이면 불필요한 복사 작업을 건너뛰기 위해 분기를 넣어주고, capacity가 다를 때만 data를 다시 할당 받도록 했다.

return 타입을 MyIntVector& 타입으로 하는 이유는 a=b=c와 같이 연쇄적으로 사용할 수 있도록 하기 위해서뿐만이 아니다. 만약 단순히 참조자 타입이 아니라 value return하면, = 연산자가 return될 때 복사 생성자로 결과 객체와 동일한 객체를 생성해낸다. , 호출될 때마다 생성자와 소멸자가 호출된다는 것이다. 지금 우리가 구현하는 간단한 클래스에서는 value return하더라도 당장 큰 문제는 없겠지만, 다른 복잡한 클래스를 구현했을 때 불필요한 오버헤드가 많이 들 수 있다.



+= 연산자는 좌항의 MyIntVector에 우항의 MyIntVector element를 이어 붙인다. 만약 우항의 객체가 비어있다면 return하고, 이어 붙일 공간이 부족할 경우 추후 설명할 reserve 함수를 호출해 capacity를 확장했다. 처음에 설계했을 때는 공간을 2배로 확장하고 이어 붙이는 작업을 진행하도록 했는데, 좌항 객체의 capacity 2배를 해도 우항 객체의 데이터를 모두 담아내지 못할 정도로 우항 객체에 데이터가 많을 때 오류가 발생했다. 그래서 충분히 확보될 때까지 2배씩 확장하는 작업을 반복하도록 코드를 구성했다.



+, -, * 연산자는 두 MyIntVector의 스칼라 합, , 곱 연산을 한다. 이 때, MyIntVector는 가지고 있는 element의 개수가 동일해야 연산이 가능하므로, 다를 때에는 오류로 판단하도록 코드를 작성하였다. 연산 결과를 tmp에 저장해 return한다.

처음에 함수를 작성할 때는 해당 연산자를 사용할 때마다 a=a+b와 같은 형식으로 사용할 것으로 생각했다. 그래서 return tmp가 대입되어 들어가게 될 경우, a data memory leak을 유발할 가능성이 있다고 생각해 a의 소멸자를 호출해줘야 한다고 생각했다. 하지만 애초에 =연산자는 좌항의 객체에 deep copy하는 것이라 아무 상관 없었다. 이런 생각이 든 이후엔 a+b의 결과로 return되는 tmp도 소멸자를 호출하지 않으면 memory leak을 유발하지 않을까 하는 생각이 들었는데, tmp는 대입 연산이 끝난 후에 함수의 지역 변수이므로 free될 것이므로 그럴 필요 없다는 결론을 내려 위와 같이 코드를 구성했다.



(Unary)- 연산자는 MyIntVector 객체의 data 안의 모든 element 값의 부호를 바꾼다. 비어있을 경우는 할게 없으므로 그대로 return하고, 그 외의 경우는 모든 원소에 -1을 곱한 값을 가지는 tmp return한다.



== 연산자는 두 객체가 완전히 동일한 데이터를 가지는지 확인한다. 두 객체의 size가 다르면 당연히 다른 객체이므로 false return한다. Size가 같으면 데이터를 하나하나 대조해서 서로 다른 값이 발견되면 false return하고, 모든 element가 같으면 true return한다.



() 연산자는 호출한 객체의 모든 element 값을 전달받은 x의 값으로 바꾼다. for문으로 객체의 모든 저장된 data의 값에 x를 대입해 return한다.



마지막으로 [] 연산자는 x번째 인덱스에 있는 element 값을 반환한다. 만약 x값이 음수이거나 element의 인덱스 범위를 넘어갈 경우는 오류이므로 프로그램을 종료하도록 했다.

 

D.     멤버함수

멤버함수는 pop_back, push_back, capacity, size, reserve, isEmpty, clear 함수를 만들었다.



pop_back 함수는 MyIntVector 객체 데이터의 맨 뒤에 있는 element를 제거하는 함수이다. 만약 데이터가 비어있으면 아무것도 하지 않고, 그 외의 경우는 size의 값을 1 줄이는 것으로 맨 뒤의 element 제거를 구현했다.



push_back 함수는 MyIntVector 객체에 데이터를 하나 삽입한다. 전달받은 x를 데이터의 맨 뒤에 추가하는 함수이다. 만약 객체에 데이터를 담을 공간이 부족하면 reserve 함수를 이용해 공간을 확보한 뒤 데이터를 삽입한다.



isEmpty 함수는 데이터가 없으면 true, 하나라도 존재하면 false를 반환한다.



멤버함수 중에서 가장 중요하다고 할 수 있는 reserve 함수는 MyIntVector capacity를 확장해주는 함수이다. 먼저 reserve 하려는 크기가 지금 저장되어 있는 element의 개수보다 작을 경우는 데이터를 잘라내려는 의도보다 잘못된 값을 입력했을 가능성이 더 높다고 판단해 그대로 return했다.

새로운 n 크기의 int형 배열을 동적으로 할당해, 새로운 배열에 기존 데이터를 copy한다. 마지막으로 새로운 배열의 주소를 data에 저장하고 기존 data delete 해주는 것으로 확장 작업을 마친다.



clear 함수는 MyIntVector 객체의 모든 데이터를 삭제한다. size 0으로 만들어주면 pop_back으로 모든 element를 제거한 것과 같은 것이므로 size 0으로 설정해줬다.



Capacity, Size 함수는 객체의 capacity 값 혹은 size 값을 반환하는 함수이다.

 


추가적으로, 이후 테스트를 진행할 때 데이터의 상태를 쉽게 확인하기 위해 데이터를 한 개씩 출력해주는 멤버함수 printElements를 만들었다.

 

3.     Main 함수


작성한 모든 연산자 및 함수가 정상적으로 동작하는지 테스트하는 코드를 작성했다.



설명하기 앞서 테스트하는 도중에 MyIntVector의 상태를 쉽게 볼 수 있도록 만든 printVectorInfo 함수를 정의해 사용했다.



먼저 MyIntVector타입 객체 vector1을 정의해 vector1의 상태를 살펴보았다.



아무것도 들어있지 않고 capacity DEFAULT_CAPACITY(==32) MyIntVector가 생성되었다.




그 후 vector1 0부터 4까지 값을 차례로 가지는 elementpush_back으로 삽입했다. vector1의 상태를 출력한 후 data 0번째 인덱스 값, 1번째 element를 출력하게 했다.



0부터 4까지 차례로 삽입되었고, 0번째 인덱스 값은 0이 맞으므로 push_back []연산자가 정상적으로 동작함을 알 수 있다.




vector2 vector1과 동일하게 생성했다.



vector2 vector1과 완전히 동일하게 생성되었음을 확인했다.




vector2 vector1을 이어 붙이는 += 연산자를 사용하고 vector2의 상태를 확인했다.



0부터 4까지의 원소가 2번 반복되는 것을 보아 정상적으로 이어 붙여졌으므로, +=연산자가 잘 동작함을 확인할 수 있다.




vector3을 생성해, 대입 연산자를 연쇄적으로 사용해보았다. 그 후, vector3 vector1 ==연산자로 비교했다.



대입 연산자를 연쇄적으로 사용해도 정상적으로 동작하며, 비교 연산자인 == 연산자도 정상적으로 동작함을 알 수 있다.




vector327개의 0을 삽입해 size 32로 만들고, 0을 하나 더 삽입하는 코드를 작성해 vector3의 상태를 확인했다.



size 32일 때 0을 하나 더 삽입했더니 capacity 2배인 64로 늘어났다. push_back을 사용했을 때 자동으로 reserve됨을 볼 수 있었다.




vector3의 모든 element 10으로 만들고, vector4 vector3과 똑같게 생성한 후 모든 원소를 1로 바꾸었다. 다음, vector3에서 vector4를 스칼라 뺄셈을 진행하고 vector3의 상태를 확인했다.



10-1 9 33개의 element가 모두 설정되어 있는 것을 보아, ()연산자와 연산자가 잘 동작함을 확인할 수 있다.




마찬가지로 + * 연산자도 테스트 해보았다.



1+1 2이고, 2*9 18이므로 모두 잘 동작함을 확인할 수 있다.




(Unary)- 연산자도 잘 동작하는지 테스트 해보았다.



18에서 -18로 바뀌어서 잘 출력되므로 (Unary)- 연산자가 잘 동작함을 확인했다.




vector3에서 한 elementpop_back으로 제거하고 vector3의 상태를 확인한 후, reserve 함수로 capacity 32로 바꾼 후 vector3의 상태를 확인한다.



Capacity 32로 줄어들었다. pop_back reserve 함수가 잘 동작하는 것을 볼 수 있다.




마지막으로 clear함수를 사용해 데이터를 모두 없애고, isEmpty로 데이터가 없는지 확인한다.



vector3에 있는 element들이 모두 제거되었다. 그러므로 clear 함수와 isEmpty 함수가 잘 동작함을 확인했다.



GitHub: https://github.com/BinZlP/MyIntVector



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

[C++] 스택을 활용한 수식 계산기  (0) 2018.12.23
[C++] 함수 및 다중 정의 (Overload)  (1) 2018.01.21
[C++] Try 구역과 예외 처리  (0) 2018.01.15
[C++] 문자열 & 벡터  (0) 2018.01.13
[C++] 변수와 기본 타입  (0) 2018.01.11
Posted by BinZIP