chapter 4 객체 포인터와 객체 배열, 객체의 동적 생성

박준우·2024년 11월 3일

명품C++프로그래밍

목록 보기
3/10

1. 객체 포인터

chapter 3에서 객체의 데이터를 접근하는 방법은 아래와 같다.

 int main(){
 	Circle donut; //Circle클래스의 donut 객체를 만든다.
	donut.radius = 1;	//맴버 변수 접근
    double area = donut.getArea(); //맴버 함수 접근
    cout << "donut의 면적은" << area <<endl;

하지만 위처럼 직접 데이터를 입력하는 경우와 달리, 포인터를 사용하면 프로그램 실행 중에 필요한 만큼 메모리를 동적(힙 공간)으로 할당할 수 있다. 특히 배열이나 객체의 크기를 컴파일 시간에 미리 정할 수 없는 경우 유용하다.

힙공간에 들어간 데이터는 직접 delete하기 전까지 영원히 그곳에 남기 때문에 힙공간을 사용했다면 반드시 delete명령을 해줘야 한다.(프로그램이 종료되면 운영체제가 자동으로 힙을 비우기는 한다.)

포인터 사용 예)
# includ <iostream>
using namespace std;

class Circle {
private:	
    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);
    Circle *p; // 클래스 명으로 포인터 변수 선언
    
    p = &donut;//포인터 p 에 donut의 주소를 입력한다. 
    cout << p->getArea(); //포인터 p를 통해 getArea()의 결과값을 출력한다. 
    //아래처럼 표기도 가능하다.
    //cout << (*p).getArea();
    
    p = &pizza;//포인터 p 에 pizza의 주소를 입력한다. 
    cout << p->getArea();//포인터 p를 통해 getArea()의 결과값을 출력한다. 

포인터에는 주소값을 입력받을 수 있다. 위 예제는 포인터에 Circle 클래스 타입의 주소를 입력받을 수 있게 힙영역에 할당하고 주소를 입력하여 그 클래스의 객체를 불러올 수 있도록 짠 코드이다.

2. 객체 배열

놀랍게도 객체를 배열로 만들 수 있다. 객체를 배열로 설정하면, 그 배열의 각각의 위치마다 객체를 주소를 저장할 수 있다.

예를 들어 Circle circleArray[3]; 이렇게 객체배열을 만들어 줬다면, circleArray라는 객체배열의 각 공간마다 주소값이나, 함수등으로 접근할 수 있다.

객체 배열 사용 예)

# includ <iostream>
using namespace std;

class Circle {
private:	
    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];
    circleArray[0].setRadius(10); // 각 공간에 맴버함수로 접근한 방식
    circleArray[1].setRadius(20);
    circleArray[2].setRadius(30);
    
    Circle *p; 
    p = circleArray; /*주소표기 &대신, 배열의 이름을 넣으면, 배열의 0번째 주소가
    				   p에 입력된다.*/

	for (i=0; i<3; i++){
    	cout << "circle" << i+1 <<"번째 배열 donut의 면적은" << p->getArea() 
        <<"입니다. <<endl; 
        p++;//포인터가 가리키는 배열의 주소를 +1한다. 
        }

객체 배열 사용시 주의할 점

객체 배열을 생성할 때 각 배열마다 (아무 매개변수가 없는) 기본 생성자를 실행한다. 만약, 매개변수가 있는 생성자를 생성하고 따로 기본생성자를 만들어 주지 않는다면, 컴파일 오류를 일으킨다.

참고로 아무 생성자도 만들어 주지않으면 기본생성자가 자동생성되어 그것을 실행함으로 오류를 일으키지 않는다.

객체 배열의 초기화

각 객체 배열을 생성할 때 아래와 같이 직접 입력하는 방법대신, 생성자를 이용하여 객체를 초기화 할 수 있다.

    circleArray[0].setRadius(10);
    circleArray[1].setRadius(20);
    circleArray[2].setRadius(30);
생성자를 이용한 객체 배열 초기화

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

3.동적 메모리 할당 및 반환

new와 delete는 실행 중에 필요한 만큼 메모리를 힙에 할당받고 프로그램이 끝나거나, delete시 반환하는 매커니즘을 구현한다.

타입 포인터변수 = new 타입
int *p = new int; //  new가 입력되었다. 
delete p; //p의 메모리를 반환한다.

동적 메모리 사용 예)

#include <iostream>
using namespace std;

int main(){
	int *p;
    p = new int; /* (힙에서 공간을 int형 만큼 할당해 포인터 p로 가리킨다.)
    
    *p = 5; //지정한 힙 위치에 5를 입력한다.+ 동적일 때는 *를 붙여준다. 
    
    delete p; // 지정한 힙을 반환한다. 

동적 할당을 배열로 하기

위처럼 1칸만 동적배열이 가능하지만 잘 사용하지 않고, 배열을 동적으로 할당한다.

배열의 동적 할당 사용예)

#include<iostream>
using namespace std;

int main(){
	cout << "입력할 정수의 개수는?" << endl;
    int n;
    cin >> n;
    int *p = new int[n]; //배열의 길이를 동적으로 할당한다.
    
    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]; // 이 문장은 sum+= *(p+i)로도 사용이 가능하다.
    
    cout << "합은 " << sum <<"입니다."
    
    delete []p; // 동적할당된 배열을 삭제하려면 delete []포인터변수 를 사용한다.
        

4. 포인터 객체의 동적 생성

단순 변수, 배열등 공간 뿐 아니라 포인터 객체도 동적으로 생성이 가능하다.

객체 동적 생성의 예)

#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;
    Circle *q;
    p = new Circle; 
    q = new Circle(30);
    
    cout << p->getArea() << endl;
    cout << q->getArea() << endl;
    
    delete p;
    delete q;
}

객체 배열의 동적 생성

배열도 동적으로 생성할 수 있다.

#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;
    pArray = new Circle[3]; //이것은 클래스 이름이다.
	
    pArray[0].setRadius(10);
    pArray[1].setRadius(20);
    pArray[2].setRadius(30);
   
    // 위 3가지 초기화는 아래처럼도 가능하다.
    Circle *pArray = new Circle[3]{ Circle(10) , Circle(20), Circle(30)}
    
	for (int i=0; i<3; i++){
    	cout <<pArray[i].getArea() << endl;
        }
    Circle *p = pArray;
    for(int i=0; i<3; i++){
    	cout << p->getArea() << endl;
        p++;
        }
    delete [] pArray;
}

5.this 포인터

this는 객체 자신에 대한 포인터이다.
첫째로 자신의 변수명을 명명할 때 사용한다. 예를 들어 아래처럼 this -> radius 를 하지 않았다면, radius가 매개변수로 가져온 것인지, 자신에게 저장된 radius인지, 컴파일러가 구분할 수 없다.

Circle(int radius) {
	this -> radius = radius;

둘째로 객체함수 자신의 주소값(힙)을 return하는 경우에 사용한다.

class sampel {
	sample *f(){
		return this;
    {
{    

주의사항

this는 동적맴버 함수에만 사용한다. 왜냐하면, 정적 맴버함수는 스택공간에 쌓이는데, 그 시점에서 this라는 현재객체는 존재하지 않을 수 있기 때문이다.

6. string클래스를 이용한 문자열 사용법

#include <string>
using namespace std;

int main(){
	string str;
    cout <<"문자열을 입력하세요" <<endl;
   	cin >> str;
    cout <<"입력 한 문자열은 이것입니다." <<endl;

위와 같이 string 패키지를 사용해주면 변수type에 문자열을 사용해 줄 수 있다.

배열에 입력받기.

string 패키지에는 getline 함수가 존재하는데, 이를 이용하면, 여러개의 문자열을 입력받아 전역공간(data)에 위치시킬 수 있다.

#include <string>
using namespace std;

int main(){
	string str[5];
	
    for(int i; i<5; i++{
    	cin >> getline(cin, str[i], '\n');
        }
        

위는 space바를 기준으로 끊어 각 string형식의 데이터를 입력가능한 str배열에 집어 넣는다.

profile
DB가 좋아요

0개의 댓글