객체 포인터
포인터란 주소값을 저장해 주소에 있는 값들을 호출하는 변수이고, 코드를 쉽게 이해하기 위해 앞으로 포인터는 "무언가를 가리키고 있는 변수 '→' "로 생각합시다.
다음은 객체 포인터를 이용한 코드입니다.
Rectangle A;
int a = A.getArea();
Rectangle *p;
p = &A;
a = p ->getArea();
Rectangle 타입의 객체에 대한 포인터 변수 p는 " Rectangle *p; " 로 선언합니다. 선언된 포인터는 주소를 지정하지 않은 한 아무 객체도 가리키지 않습니다. (객체 생성 시 자동으로 그 객체를 가리키지 않습니다.)
※ 따라서 초기화를 하지 않는다면 객체에 대한 멤버 함수 접근이 불가하기에 오류가 발생합니다.
객체 주소 지정은 "& 연산자"를 사용해 " p = &A; " 로 지정하며 포인터 선언 시에 초기화를 할 수도 있습니다. " Rectangle *p = &A; "
int a = A.getArea(); 처럼 객체가 멤버 함수에 접근할 때는 ". 연산자 "를 사용하지만, 객체 포인터로 멤버 함수에 접근할 때는 " -> 연산자 "를 사용합니다. " a = p -> getArea(); " 혹은 " a = (*p). getArea(); " 로 선언할 수 있습니다.
객체 배열
객체 배열은 원소가 객체인 배열이며 나머지는 일반적인 배열 개념과 동일합니다.
다음은 객체 배열을 이용한 코드입니다.
Rectangle rectangle_array[3];
rectangle_array[0].valueIn(10); //valueIn(int n)과 getArea()은 클래스에 선언된 맴버 함수라고 합시다.
rectangle_array[1].valueIn(20);
rectangle_array[2].valueIn(30);
// 출력
for(int i=0; i<3; i++)
cout << rectangle_array[i].getArea(); << endl;
Rectangle *p;
p = rectangle_array;
for(int i=0; i<3; i++){
cout << p->getArea() << endl;
p++;
}
객체 배열 선언은 객체이름을 타입으로 적고 배열을 선언하면 됩니다. ( " Rectangle rectangle_array [3]; " ) 이때 배열은 객체를 원소로 가지므로 위의 코드에서는 총 3개의 객체를 저장할 수 있는 배열이 만들어지는 겁니다.
일반적인 배열과 비슷하게 객체 포인터를 p = rectangle_array로 지정한다면 포인터는 rectangle_array[0]을 가리킵니다.
객체 배열 선언시 객체들의 생성자는 어떻게 되나요?
매개변수를 받는 방식으로 생성자를 지정할 수 있지만, 객체 배열로 선언된 객체들은 매개 변수 없는 기본 생성자를 호출합니다.
때문에 클래스에서 아무 생성자가 없다면 객체 배열이 만들어질 때 자동으로 생성자를 만들어 컴파일 오류가 발생하지 않지만, 매개 변수가 있는 생성자만 선언한다면 매개 변수가 없는 생성자가 자동으로 만들어지지 않아 컴파일 오류가 발생할 것입니다.
※ 앞으로 객체 배열을 선언할 때 생성자를 필수적으로 확인해야 합니다.
맴버 함수의 호출은 객체를 생성 시 ". 연산자"를 사용했던 것처럼 아래와 같이 작성하면 됩니다.
rectangle_array[0].valueIn(10);
rectangle_array[1].valueIn(20);
rectangle_array[2].valueIn(30);
다음으로 객체 배열을 생성할 때 생성자를 사용하여 원소 객체를 초기화할 수 있습니다. rectangle(10), rectangle(20)은 생성자 rectangle(int n)을 호출합니다.
Rectangle rectangle_array[3] = { rectangle(10), rectangle(20), rectangle() };
마지막으로 함수 종료 시 객체 배열이 소멸할 때 각 원소 객체마다 소멸자가 호출됩니다. 이때 높은 인덱스부터 ~rectangle(); 가 실행됩니다.
- 객체 포인터를 이용한 객체 배열 사용
객체 포인터를 이용해 객체 배열을 호출하려면 p = rectangle_array로 지정 시 " p-> getArea() " 하면 rectangle_array [0] 객체의 getArea() 멤버 함수에 접근할 수 있다는 점을 이용합니다.
또한 p [i]는 배열 i 번째 rectangle 객체라는 점을 생각하면 " p [0]. getArea(); "이나 " (*p). getArea(); " 같은 형식으로도 rectangle 객체의 getArea() 함수를 호출할 수 있습니다.
- 다차원 객체 배열
일차원 배열에 차원하나를 더 사용하며 변하는 개념은 없습니다.
Rectangle retangle_array[2][3]; // 2차원 객체 배열 생성
rectangle_array[0][0].valueIn(10);
rectangle_array[1][1].valueIn(20);
rectangle_array[1][2].valueIn(30);
...
똑같이 클래스 이름으로 객체 배열을 생성하고, ". 연산자 "를 사용해 멤버 함수를 호출합니다.
Rectangle rectangle_array[2][3] = { {rectangle(10), rectangle(20), rectangle() },
{ {rectangle(30), rectangle(40), rectangle()} };
위와 같이 초기화도 일반적인 배열을 초기화하는 방식으로 하면 됩니다.
동적 메모리 할당 및 반환
간단한 코드를 작성할 때는 변수, 객체, 배열을 정적으로 선언해 필요한 메모리를 확보할 수 있지만, 복잡한 코드를 작성할 때는 어느 정도의 메모리가 필요한지 가늠이 안 갈 때가 있습니다.
이때 사용하는 것이 "동적 메모리 할당/반환 메커니즘"이며, C언어에서는 주로 malloc()/free()등의 표준 C함수를 이용하지만, C++에서는 새로운 연산자를 사용해 메모리를 동적으로 할당합니다.
다음은 메모리를 할당받고 반환하는 코드입니다.
int *point_int = new int; // int 타입의 정수 공간 할당
char *point_char = new char; // char 타입의 문자 공간 할당
Rectangle *point_rectangle = new rectangle(); // Rectangle 클래스 타입의 객체 할당
delete point_int; // 할당된 정수 공간 반환
delete point_char; // 할당된 정수 공간 반환
delete point_rectangle; // 할당된 정수 공간 반환
new 연산자는 " 데이터 타입 *포인터변수 = new 데이터타입 " 으로 데이터타입의 크기만큼 힘으로부터 메모리를 할당받고 주소를 리턴합니다. 이렇게 되면 포인터 변수는 할당받은 메모리의 주소를 가지게 됩니다. 이때 데이터 타입은 구조체나 클래스도 포함합니다.
" 데이터 타입 *포인터변수 = new 데이터타입(초기값) " 으로 동적 할당 메모리를 초기화할 수 있습니다.
※ 만약 힙메모리가 부족하면 NULL값을 리턴하므로, if(! point) return; 같이 메모리 할당이 실패한 경우를 대비해야 합니다.
delete 연산자는 " delete 포인터 변수 " 로 포인터변수가 가리키는 메모리를 힙으로 반환합니다. delete를 사용하는 기본전제는 메모리가 동적으로 할당된 포인터 변수를 사용하는 것이기에 포인터 변수가 new로 공간 할당을 받았는지 확인해야 합니다. 또한 한번 반환된 포인터 변수를 중복해서 반환할 수 없습니다.
new와 delete 연산자를 이용해 배열을 할당받고 반환할 수 있습니다. 이때 new 연산자는 배열의 크기만 한 배열을 할당받아 주소를 리턴합니다.
데이터 타입 *포인터변수 = new 데이터타입[배열의 크기]; // 배열의 동적 할당
delete [] 포인터변수; // 배열 메모리 반환
아래는 배열을 할당받고 반환하는 기초 코드입니다.
int *p= new int [10]; // 크기가 10인 정수형 배열의 동적 할당
if(!p)
return; // 메모리 할당 실패 대비
for(int i=0; i<10; i++)
p[i]=i; // *(p+i) = i; 와 동일
delete [] p; // 배열 메모리 반환
※ new로 배열을 동적 할당받을 때 직접 초깃값을 지정할 수 없습니다. 즉, new int [10](1)은 안되지만 new int [] {1,2,3,4,5...}; 같은 형식은 가능합니다.
'프로그래밍 > C++' 카테고리의 다른 글
[C++] 5장. #1 함수 호출 시 객체 전달 (2) | 2023.12.08 |
---|---|
[C++] 4장. #2 this 포인터와 string 클래스 (0) | 2023.12.08 |
[C++] 3장. #2 접근 지정자와 인라인 함수 (1) | 2023.12.03 |
[C++] 3장. #1 클래스와 객체 (0) | 2023.12.02 |
[C++] 2장. #2 입력 받기 (0) | 2023.11.30 |