1. 객체 배열과 객체 포인터
1) 객체 배열 및 포인터의 생성 및 사용
- 기본 타입 배열 선언과 형식 동일
- 객체 배열 정의 시 따로 지정하지 않으면 항상 디폴트 생성자로 초기화
Circle circleArray[3]; //디폴트 생성자로 초기화
- 배열의 각 원소 객체 당 생성자를 지정하는 방법
- { } 안에 생성자 나열
Circle circleArray[3]={Circle(10), Circle(20), Circle()}
// 0번 객체 생성될 때, 생성자 Circle(10) 호출
// 1번 객체 생성될 때, 생성자 Circle(20) 호출
// 2번 객체 생성될 때, 생성자 Circle() 호출

- Ex) Circle 클래스의 배열 선언 및 활
#include <iostream>
using namespace std;
class Circle {
int radius;
public:
Circle() { radius = 1; }
Circle(int r) { radius = r; }
void setRadius(int r) { radius = r; }
double getArea();
};
double Circle::getArea()
{
return 3.14*radius*radius;
}
int main() {
Circle circleArray[3]; // (1) Circle 객체 배열 생성
// 배열의 각 원소 객체의 멤버 접근
circleArray[0].setRadius(10); // (2)
circleArray[1].setRadius(20);
circleArray[2].setRadius(30);
for(int i=0; i<3; i++) // 배열의 각 원소 객체의 멤버 접근
cout << "Circle " << i << "의 면적은 " << circleArray[i].getArea() << endl;
Circle *p; // (3)
p = circleArray; // (4)
for(int i=0; i<3; i++) { // 객체 포인터로 배열 접근
cout << "Circle " << i << "의 면적은 " << p->getArea() << endl;
p++; // (5)
}
}

#include <iostream>
using namespace std;
class Circle {
int radius;
public:
Circle() {radius = 1; }
Circle(int r) { radius = r; }
void setRadius(int r) { radius = r; }
double getArea();
};
double Circle::getArea()
{
return 3.14*radius*radius;
}
int main() {
Circle circleArray[3] = { Circle(10), Circle(20), Circle() }; // Circle 배열 초기화
for(int i=0; i<3; i++)
cout << "Circle " << i << "의 면적은 " << circleArray[i].getArea() << endl;
}

2) 객체 포인터
- 객체의 주소 값을 가지는 변수
- '객체 포인터 -> 변수' 로 접근

#include <iostream>
using namespace std;
class Circle {
int radius;
public:
Circle() {radius = 1; }
Circle(int r) { radius = r; }
double getArea();
};
double Circle::getArea() {
return 3.14*radius*radius;
}
int main() {
Circle donut;
Circle pizza(30);
// 객체 이름으로 멤버 접근
cout << donut.getArea() << endl;
// 객체 포인터로 멤버 접근
Circle *p;
p = &donut;
cout << p->getArea() << endl; // donut의 getArea() 호출
cout << (*p).getArea() <<endl; // donut의 getArea() 호출
p = &pizza;
cout << p->getArea() << endl; // pizza의 getArea() 호출
cout << (*p).getArea() << endl; // pizza의 getArea() 호출
}

2. 동적메모리 할당 및 반환
1) 동적 메모리의 필요성
- 동적 메모리
- 프로그램 실행 중에 메모리의 할당과 해제가 결정되는 메모리
- 동적 메모리의 장점
- 메모리 낭비 해결
- 실행 중에 필요한 만큼만 메모리를 할당 받아서 사용
- 미리 정해진 크기가 아니라 원하는 크기만큼 할당 받기 가능
- 메모리의 할당과 해제 시점을 전적으로 프로그래머가 제어 가능
- 메모리 낭비 해결
2) 정적 할당과 동적 할당
- 정적 할당
- 변수 선언을 통해 필요한 메모리 할당
- 많은 양의 메모리는 배열 선언을 통해 할당
- 변수 선언을 통해 필요한 메모리 할당
- 동적 할당
- 필요한 양이 예측되지 않는 경우, 프로그램 작성 시 할당 받을 수 없으므로 실행 중에 운영체제로부터 할당 받음
- heap으로부터 할당
- heap: 모든 프로세스가 공유할 수 있는 메모리로 운영체제가 소유하고 관리
- heap으로부터 할당
- 필요한 양이 예측되지 않는 경우, 프로그램 작성 시 할당 받을 수 없으므로 실행 중에 운영체제로부터 할당 받음

3) new와 delete
- C++의 기본 연산자로 동적 메모리 사용 시 선언
- 형식

int *pInt = new int; // int 타입의 메모리 동적 할당
char *pChar = new char; // char 타입의 메모리 동적 할당
Circle *pCircle = new Circle(); // Circle 클래스 타입의 메모리 동적 할당
delete pInt; // 할당 받은 정수 공간 반환
delete pChar; // 할당 받은 문자 공간 반환
delete pCircle; // 할당 받은 객체 공간 반환

- delete 주의 사항
- 동적으로 할당된 메모리 주소를 저장하는 포인터 변수가 없어지는 것이 아님
- 따라서 delete 연산자로 동적 메모리를 해제한 다음에 동적 메모리의 주소를 저장하는 포인터 변수를
NULL로 지정하는 것이 안전 - 동적으로 할당 받지 않는 메모리 반환하면 오류 발생
- 동일한 메모리를 두 번 반환하면 오류 발생
- Ex) 정수형 공간의 동적 할당 및 반환
#include <iostream>
using namespace std;
int main()
{
int *p;
p = new int;
if(!p) {
cout << "메모리를 할당할 수 없습니다.";
return 0;
}
*p = 5; // 할당 받은 정수 공간에 5 삽입
int n = *p;
cout << "*p = " << *p << endl;
cout << "n = " << n << endl;
delete p;
}

4) 배열의 동적 할당 및 반환
- new/delete 연산자의 사용 형식


#include <iostream>
using namespace std;
int main()
{
cout << "입력할 정수의 개수는?";
int n;
cin >> n; // 정수의 개수 입력
if(n <= 0) return 0;
int *p = new int[n]; // n 개의 정수 배열 동적 할당
if(!p) {
cout << "메모리를 할당할 수 없습니다.";
return 0;
}
for(int i=0; i<n; i++) {
cout << i+1 << "번째 정수: "; // 프롬프트 출력
cin >> p[i]; // 키보드로부터 정수 입력
}
int sum = 0;
for(int i=0; i<n; i++)
sum += p[i];
cout << "평균 = " << sum/n << endl;
delete [] p; // 배열 메모리 반환
}

- 동적 할당 메모리 초기화
- 동적 할당 시 초기화 가능
- 배열의 경우, 동적 할당 시 초기화 불가
int *pInt = new int(20); // 20으로 초기화된 int 타입 할당
char *pChar = new char('a'); // ‘a’로 초기화된 char 타입 할당
int *pArray = new int [10](20); // 구문 오류. 컴파일 오류 발생
int *pArray = new int(20)[10]; // 구문 오류. 컴파일 오류 발생
- delete시 [ ]생략
- 컴파일 오류는 아니지만 비정상적인 반환이므로 생략하지 않도록 주의
int *p = new int [10];
delete p; // 비정상 반환. delete [] p;로 하여야 함.
int *q = new int;
delete [] q; // 비정상 반환. delete q;로 하여야 함.
- 메모리 누수

3. 객체 및 객체 배열의 동적 생성 및 반환
1) 객체의 동적 생성 및 반환


#include <iostream>
using namespace std;
class Circle {
int radius;
public:
Circle();
Circle(int r);
~Circle();
void setRadius(int r) { radius = r; }
double getArea() { return 3.14*radius*radius; }
};
Circle::Circle() {
radius = 1;
cout << "생성자 실행 radius = " << radius << endl;
}
Circle::Circle(int r) {
radius = r;
cout << "생성자 실행 radius = " << radius << endl;
}
Circle::~Circle() {
cout << "소멸자 실행 radius = " << radius << endl;
}
int main() {
Circle *p, *q;
p = new Circle;
q = new Circle(30);
cout << p->getArea() << endl << q->getArea() << endl;
delete p;
delete q;
}

2) 객체 배열의 동적 생성 및 반환
- 동적으로 생성된 배열도 보통 배열처럼 사용

#include <iostream>
using namespace std;
class Circle {
int radius;
public:
Circle();
Circle(int r);
~Circle();
void setRadius(int r) { radius = r; }
double getArea() { return 3.14*radius*radius; }
};
Circle::Circle() {
radius = 1;
cout << "기본생성자 radius = " << radius << endl;
}
Circle::Circle(int r) {
radius = r;
cout << "인자생성자 radius = " << radius << endl;
}
Circle::~Circle() {
cout << "소멸자 radius = " << radius << endl;
}
int main() {
Circle *pArray = new Circle [3]; // 객체 배열 생성
pArray[0].setRadius(10);
pArray[1].setRadius(20);
pArray[2].setRadius(30);
for(int i=0; i<3; i++) {
cout << pArray[i].getArea() << endl;
}
Circle *p = pArray; // 포인터 p에 배열의 주소값으로 설정
for(int i=0; i<3; i++) {
cout << p→getArea() << endl;
p++; // 다음 원소의 주소로 증가
}
delete [] pArray; // 객체 배열 소멸
}
3) 객체에 대한 포인터 배열의 생성 및 사용
- 객체의 주소를 저장하는 포인터 배열
Point *arr[3] = { new Point(10, 10), new Point(20, 20), new Point(30, 30) };
for(int i = 0 ; i < 3 ; i++ )
arr[i]->Print(); // arr[i]는 객체에 대한 포인터이므로 → 사용
for(int i = 0 ; i < 3 ; i++ )
delete arr[i];
4. 멤버함수의 this 포인터
1) this 포인터
- 포인터, 멤버 함수를 소유한 객체를 가리키는 포인터
- 클래스의 멤버 함수 내에서만 사용
- 개발자가 선언하는 변수가 아니고, 컴파일러가 선언한 변수
- 멤버 함수에 컴파일러에 의해 묵시적으로 삽입 선언되는 매개 변수
- 각 객체의 this는 다른 객체의 this와 다름
class Circle {
int radius;
public:
Circle() { this→radius=1; }
Circle(int radius) { this→radius = radius; }
void setRadius(int radius) { this→radius = radius; }
....
};

- this 포인터의 장점
- 매개 변수의 이름과 멤버 변수의 이름이 같은 경우
- 멤버 함수가 객체 자신의 주소를 리턴할 때


- this 제약 사항
- 멤버 함수가 아닌 함수에서 this 사용 불가
- 객체와의 관련성이 없기 때문에
- static 멤버 함수에서 this 사용 불가
- 객체가 생기기 전에 static 함수 호출이 존재할 수 있기 때문에
- 멤버 함수가 아닌 함수에서 this 사용 불가
'프로그래밍 언어 > C++' 카테고리의 다른 글
9) friend와 연산자 중복 (0) | 2023.12.08 |
---|---|
8) 함수와 참조, 복사생성자 (0) | 2023.12.08 |
6) 접근 지정자, static 멤버 (0) | 2023.11.02 |
5) 생성자와 소멸자 (0) | 2023.11.01 |
4) 클래스와 객체의 기본 (1) | 2023.11.01 |