본문 바로가기
Language & Framework & GIT/C++

[씨앤씨뿔]C++/ 객체 포인터& 배열& 동적 생성 - 1

by veganwithbacon 2023. 4. 24.
반응형

  객체 포인터

 

  객체에 대한 포인터 변수 선언

Circle *p;

현재 선언된 포인터 변수 p는 아무 객체도 가리키지 않는다

 

  포인터 변수에 객체 주소 지정

p = &donut; 

포인터 변수 선언 시 객체 주소로 초기화

Circle* p = &donut; 

 

  포인터를 이용한 객체 멤버 접근

객체 이름으로 멤버에 접근할 때는 다음과 같이 점(.) 연산자를 이용하지만,

d = donut.getArea(); //객체 이름으로 멤버 함수 호출

객체 포인터로 멤버 접근할 때는 -> 연산자를 사용한다.

다음 코드는 p가 가리키는 객체의 getArea()함수를 호출한다.

d = p->getArea(); //포인터로 객체 멤버 함수 호출
d = (*p).getArea(); //위 코드와 같은 의미

 

  CheckTime

Polygon poly;

Polygon *p;

 

1)포인터 p를 활용해 poly객체의 draw()함수를 호출하는 코드를 두 줄로 작성해라

p= &poly;

cout << p->draw() << endl;

cout << (*p).draw() <<endl;

 

2) 4번

 


  객체 배열

 

  객체 배열 선언

Circle circleArray[3];

위와 같이 선언한다

 

  객체 배열 선언문은 기본 생성자를 호출

객체 배열 선언문은 오직 매개 변수가 없는 기본 생성자를 호출한다

Circle circleArray[3];

3개의 객체 생성 후, 각 객체마다 다음의 기본 생성자가 호출

Circle::Circle(){radius=1;}

 

컴파일러가 자동으로 기본 생성자를 삽입하므로, 객체 배열 생성 시 컴파일 오류가 발생하지 않는다.

 

매개변수가 있는 생성자가 있는데, 기본 생성자를 선언하지 않는다면 컴파일 에러가 발생한다.

 

  객체 배열 사용

배열의 각 원소 객체는 [ ]연산자로 구분한다

원소 객체와 멤버 사이에 점(.) 연산자를 사용하여 멤버 함수를 호출한다.

 

for(int i=;i<3;i++)
	cout << "Circle" << i << "의 면적은 "<< circleArray[i].getArea() << endl;

동일한 기능을 해도 포인터를 통해 위의 코드를 아래와 같이 바꿀 수 있다.

Circle *p;
p- circleArray;
for(int i=0;i<3;i++){
	cout << "Circle " << i << "의 면적은 " << p->getArea() <<endl;
    p++;
}

  배열 소멸과 소멸자

함수가 종료하면 함수 내에 선언된 배열도 소멸된다.

배열이 소멸되면, 모든 원소 객체가 소멸되며 각 원소 객체마다 소멸자가 호출된다. 

main()함수가 종료하면 circleArray배열이 소멸되며, 높은 인덱스에서부터 원소 객체가 소멸되고, 각 객체마다 ~Circle()소멸자가 실행된다.

 

circleArray[2] 소멸자 진행 -> circleArray[1] 소멸자 진행 -> circleArray[0] 소멸자 진행

 

객체 포인터를 통해 객체 배열을 다루는 다양한 방법

더보기

객체 포인터를 이용해 circleArray의 각 Circle객체의 면적을 추력하는 코드를 다양하게 작성 가능

 

1)포인터 p를 이용해 객체처럼 접근

Circle *p = circleArray;

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

    cout << (*p++).getArea() << endl;

 

2) 배열의 이름 circleArray를 포인터로 사용

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

   cout << (circleArray+i)->getArea() << endl;

 

3) 포인터 p의 정수 연산 이용

Circle *p = circleArray;

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

    cout << (p+i) -> getArea();

 

  객체 배열 초기화

객체 배열 생성 시 생성자를 사용해 다음과 같이 원소 객체 초기화가 가능하다.

Circle circleArray[3] = { Circle(10), Circle(20), Circle() };

 


  다차원 객체 배열

Circle circles[2][3];

2차원 배열도 일차원 배열과 동일하게, 각 원소 객체가 생성될 때 기본 생성자 Circle()이 실행되어 모든 객체의 radius 값이 1이 된다. 각 객체의 radius 값을 1에서 6까지로 초기화하고 싶다면 다음과 같이 진행

circles[0][0].setRadius(1);

circles[0][1].setRadius(2);

circles[0][2].setRadius(3);

circles[1][0].setRadius(4);

circles[1][1].setRadius(5);

circles[1][2].setRadius(6);

 

다음과 같이 { } 안에 생성자를 지정해 배열을 초기화할 수 있다.

배열의 초기화된 결과는 이와 같다

Circle circles[2][3] = { { Circle(1), Circle(2), Circle(3) },

                                    { Circle(4), Circle(5), Circle(6) } };

 


  CHECKTIME

class Sample{
	int a;
public:
	Sample(){ a = 100; cout << a << ' ';}
    Sample(int x) { a = x; cout << a << ' ';}
    Sample(int x, int y){ a = x*y; cout << a << ' ';}
    int get() { return a;}
 };

1) Sample arr[3]; 이 실행될 때 출력되는 결과는?

3

 

2) Sample arr2D [2] [2] = { {Sample(2, 3), Sample(2, 4) }, {Sample(5), Sample()} };이 실행될 때 출력되는 결과는?

6 8 5 100

 

3) 객체 포인터를 이용해 (1)에서 선언된 arr의 모든 원소(a)의 합을 출력하는 for문을 작성하라.

for(int i=0; i<2; i++){

int SumA +=arr[i];

}

 

4) (2)에서 선언된 arr2D 배열 이름을 이용해 모든 원소(a)의 합을 출력하는 for문을 작성하라

for(int i=0; i<2; i++){

   for(int j=0; j<2; j++){

        int cnt +=arr2D[i][j].get();

    }

}


  동적 메모리 할당 및 반환

 

동적 메모리 할당

: 보통 개발자는 프로그램 작성 단계에서 필요한 메모리 확보를 위해 변수, 객체, 배열을 정적으로 선언한다.

 

실행 중에 필요한 만큼 메모리를 할당받고 필요 없을 때 반환하는 '동적 메모리 할당/ 반환 메커니즘'이 필요하다.

이처럼 C에서는 동적 메모리 할당 및 반환을 위해 malloc()/ free()등에 표준 C를 사용하지만, C++에서는 new와 delete연산자를 이용한다. new 연산자는 힙(heap)이라는 공간으로부터 메모리를 할당받고, delete 연산자는 할당받은 메모리를 힙으로 반환한다.

 

  new와 delete 연산자

 = new와 delete의 기본 형식

데이터타입 *포인터변수 = new 데이터타입;
delete 포인터변수;

 

  new 연산자

 : '데이터타입'의 크기만큼 힙으로부터 메모리를 할당받고 주소를 리턴

'포인터변수'는 할당받은 메모리의 주소를 가진다

  delete 연산자

: '포인터변수'가 가리키는 메모리를 힙으로 반환

 

'데이터타입'은 int, char, double 등 기본 타입뿐 아니라 구조체(struct), 클래스(class)도 포함한다

int *pInt = new int; //int타입에 정수공간할당
char *pChar = new char; //char 타입의 문자 공간 할당
Circle *pCircle = new Circle(); //cIRCLE 클래스 타입의 객체 할당

delete pInt; //할당받은 정수 공간 반환
delete pChar;  //할당받은 문자 공간 반환
delete pCircle; //할당받은 객체 공간 반환

힙 메모리가 부족하면 new는 NULL을 리턴하므로, new의 리턴 값이 NULL인지 검사하는 것이 좋다.

 

int *p = new int; //힙으로부터 int 타입의 정수 공간 할당
if(!p) {  // if(p==NULL)과 동일. p가 NULL이면
	return;  //메모리 할당받기 실패
}

*p = 5; //할당받은 정수공간에 5 기록
int n = *p; //할당받은 정수 공간에서 값 읽기, n=5;
delete p;  //할당받은 정수 공간 반환

 

  동적 할당 메모리 초기화

데이터타입 *포인터변수 = new 데이터타입(초깃값);

 

 

  delete 사용 시 주의

동적 할당받지 않는 메모리를 반환해 실행 오류가 발생

int n;
int *p = &n;
delete p; //실행 오류. p가 가리키는 메모리는 동적 할당받은 것이 아님

마찬가지로 동일 메모리를 두 번 반환시 실행 오류가 발생

int *p = new int;
delete p; //정상적인 메모리 반환
delete p; //이미 메모리 반환을 했기에 실행 오류 발생

 

  배열을 초기화할 때 주의 사항

new로 배열을 동적 할당받을 때 다음과 같이 생성자를 통해 직접 '초깃값'을 지정할 수 없다.

int *pArray = new int[10](20); //구문 오류. 배열의 초기화는 불가

int *pArray = new int(20)[10]; //구문 오류

 

다음과 같이 초깃값을 지정 가능

int *pArray = new int [ ] {1,2,3,4} // 1, 2, 3, 4로 초기화된 정수 배열 생성

 

  배열을 delete할 때 주의 사항

배열을 반환할 때 delete연산자의 사용에 주의

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

int *p = new int [10];

delete p; //비정상 반환, delete [ ] p; 로 해야함

int *q = new int;

delete [ ] q; //비정상 반환. delete q; 로 해야함

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

반응형

댓글