객체 포인터(Object Pointer)
// 예제 4-1
#include <iostream>
using namespace std;
class Circle
{
int radius;
public:
Circle();
Circle(int r);
double getArea();
};
Circle::Circle() { radius = 1; }
Circle::Circle(int r) { radius = r; }
double Circle::getArea() { return 3.14 * radius * radius; }
int main()
{
Circle donut;
Circle pizza(30);
cout << donut.getArea() << endl;
Circle* p1 = &donut;
cout << p1->getArea() << endl;
cout << (*p1).getArea() << endl;
Circle* p2 = &pizza;
cout << p2->getArea() << endl;
cout << (*p2).getArea() << endl;
}
C++에서는 객체를 다루기 위해 객체에 대한 포인터 변수를 선언하고, 이 포인터 변수로 객체의 멤버 변수를 읽고 값을 쓰거나, 멤버 함수를 호출할 수 있다.
Circle donut;
double d = donut.getArea();
Circle *p; // 객체에 대한 포인터 선언
p = &donut; // 포인터 변수 p에 객체 donut의 주소 저장
// Circle *p = &donut으로 초기화 가능
d = p -> getArea(); // 멤버 함수 호출
// d = (*p).getArea(); 로 초기화 가능
해당 코드는 Circle 객체에 대한 포인터 변수 p를 선언하고 활용하는 사례다. 객체의 주소는 객체 이름 앞에& 연산자를 사용하여 표현한다. 객체 포인터로 멤버를 접근할 때 ->연산자를 이용한다.
요약
객체를 다루기 위해 객체 포인터를 사용할 수 있으며, 멤버 접근을 위해 -> 연산자를 사용한다.
객체 배열(Object Array)
객체 배열은 원소가 객체라는 점을 제외하곤 기본 타입의 배열을 선언하고 활용하는 방법과 동일하다.
// 예제 4-2
#include <iostream>
using namespace std;
class Circle
{
int radius;
public:
Circle();
Circle(int r);
void setRadius(int r);
double getArea();
};
Circle::Circle() { radius = 1; }
Circle::Circle(int r) { radius = r; }
void Circle::setRadius(int r) { radius = r; }
double Circle::getArea() { return 3.14 * radius * radius; }
int main()
{
Circle circleArray[3];
for (int i = 0; i < 3; i++)
{
circleArray[i].setRadius(10 * (i + 1));
cout << "Circle " << i << "의 면적은 " << circleArray[i].getArea() << endl;
}
Circle* p = circleArray;
for (int i = 0; i < 3; i++)
{
cout << "Circle " << i << "의 면적은 " << p->getArea() << endl;
p++;
}
}
객체 배열 선언문은 오직 매개 변수 없는 기본 생성자를 호출한다.
- 따라서 Circle circleArray[3]가 실행되면 3개의 Circle 객체가 생성되고, 각 객체마다 기본 생성자가 호출되는 것이다.
- 만약 매개 변수를 갖는 생성자가 선언되어 있으면, 기본 생성자가 선언되지 않기 때문에 오류가 발생한다.
- 따라서 객체 배열을 선언할 때 기본 생성자가 있는지 확인해야 한다.
- 함수가 종료하면 함수 내에 선언된 배열도 소멸된다. 이때 배열이 소멸되면, 모든 객체가 소멸되어 높은 인덱스부터 낮은 인덱스까지의 원소 객체마다 소멸자가 호출된다.
객체 포인터를 사용해 객체 배열을 다루는 방법은 여러가지다.
- 포인터를 이용
- 배열의 이름을 포인터로 사용
- 포인터의 정수 연산 이용
// 예제 4-3
#include <iostream>
using namespace std;
class Circle
{
int radius;
public:
Circle();
Circle(int r);
void setRadius(int r);
double getArea();
};
Circle::Circle() { radius = 1; }
Circle::Circle(int r) { radius = r; }
void Circle::setRadius(int r) { radius = r; }
double Circle::getArea() { return 3.14 * radius * radius; }
int main()
{
Circle circleArray[3] = { Circle(10), Circle(20), Circle() };
for (int i = 0; i < 3; i++)
{
cout << "Circle " << i << "의 면적은 " << circleArray[i].getArea() << endl;
}
}
다음 예제처럼 객체 배열을 초기화할 때 생성자를 사용해 원소 객체를 초기화할 수 있다.
요약
객체 배열
1. 정의: 배열의 원소가 객체인 배열
2. 특징
a. 오직 매개 변수가 없는 기본 생성자만 호출
b. 객체가 소멸될 때 높은 인덱스부터 낮은 인덱스순으로 소멸자가 호출됨
동적 메모리 할당 및 반환
// 예제 4-5
#include <iostream>
using namespace std;
int main()
{
int* p = new int;
if (!p) // !p와 p == NULL은 같은 의미
{
cout << "메모리를 할당할 수 없습니다." << endl;
return 0;
}
*p = 5; // 할당 받은 정수 공간에 5 기록
int n = *p; // 할당 받은 정수 공간에서 값 읽기
cout << "*p = " << *p << endl;
cout << "n = " << n << endl;
delete p;
}
new 연산자: 자료형의 크기만큼 힙으로부터 메모리를 할당받고 주소를 반환하는 연산자.
- 이때 기본 타입뿐 아니라, 구조체와 클래스도 포함한다.
- 힙 메모리가 부족하다면 new가 NULL을 반환하므로, 이를 검사하는 것이 좋다.
delete 연산자: 포인터 변수가 가리키는 메모리를 힙으로 반환하는 연산자.
- 동일한 메모리를 두 번 반환하거나, 적절하지 못한 포인터를 사용하면 실행 오류가 발생한다.
- delete를 하고 난 후 포인터 변수는 살아있지만, 메모리는 반환한다.
<자료형> *<포인터 변수> = new <자료형>(초깃값);
delete <포인터 변수>
<자료형> *<포인터 변수> = new <자료형>[배열의 크기];
delete [] <포인터 변수>;
배열을 동적 할당할 경우 생성자를 통해 직접 초깃값을 지정할 수 없음에 유의할 것.
요약
동적 할당
1. 정의: 자료형의 크기만큼 힙으로부터 메모리를 할당받고 주소를 반환하는 연산자. C++에서는 new로 할당받고 delete로 할당 해제함
2. 특징
a. 힙 메모리가 부족하다면 new가 NULL을 반환함
b. delete를 하고 난 후 포인터 변수는 살아있지만, 메모리는 반환됨.
문제풀이: GitHub - maloveforme/Cpp
반응형
'Language > C++' 카테고리의 다른 글
[C++] CH5 함수와 참조, 복사 (1) (0) | 2023.02.28 |
---|---|
[C++] CH4 객체 포인터, 배열, 동적 할당 (2) (0) | 2023.02.03 |
[C++] CH3 클래스와 객체 (2) (0) | 2023.01.30 |
[C++] CH3 클래스와 객체 (1) (0) | 2023.01.28 |
[C++] CH2 C++ 프로그래밍의 기초 (0) | 2023.01.27 |