C++ 객체포인터,동적생성

ChoRong0824·2023년 4월 15일
0

C

목록 보기
14/17
post-thumbnail

객체 포인터 부분.
중요한 부분이라 가독성을 위해, 따로 포스팅하게 되었습니다.

객체 포인터

객체에 대한 포인터

  • c 언어의 포인터와 동일
  • 객체의 주소 값을 가지는 변수

포인터로 멤버를 접근할 때

  • 객체포인터 -> 멤버
Circle donut;
double d = donut.getArea();

Circle* p;	// 객체에 대한 포인터 선언
p = &donut;	// 포인터에 객체 주소 저장
d = p -> getArea(); // 멤버함수 호출

예제 4-1, 객체 포인터 선언 및 활용

#include <iostream>

using namespace std;

class Circle
{
private:
    int radius; // 원의 반지름을 저장하는 변수
public:
    // 매개변수가 없는 디폴트 생성자
    Circle(){
        radius= 1; // 반지름 변수를 1로 초기화
    }

    // int형 매개변수를 받는 생성자
    Circle(int r){
        radius = r; // 매개변수로 받은 값을 반지름 변수에 할당
    }

    // 원의 넓이를 계산하여 반환하는 함수
    double getArea();
    ~Circle();
};

Circle::Circle(){
}

double Circle::getArea(){
    return 3.14*radius*radius;
}

Circle::~Circle()
{
}


int main(){
    Circle donut;
    Circle pizza(30);

    // 객체 이름으로 멤버 접근
    cout << donut.getArea() << endl;

    // 객체 포인터로 멤버 접근
    Circle *p;
    p = &donut;
    cout << p ->getArea() << endl; // 도넛의 getArea 호출
    cout << (*p).getArea() << endl; // 도넛의 getArea 호출


    p = &pizza;
    cout << p -> getArea() << endl;
    cout << (*p).getArea() << endl; 

    return 0;
}

객체 배열, 생성 및 소멸

객체 배열 선언 가능

  • 기본타입 배열 선언과 형식이 동일함
    - int n[3]; // 정수형 배열 선언
    - Circle c[3]; // Circle 타입의 배열 선언

객체 배열 선언

  1. 객체 배열을 위한 공간 할당
  2. 배열의 각 원소 객체마다 생성자 실행
    • c[0]의 생성자, c[1]의 생성자, c[2]의 생성자 실행
    • 매개 변수 없는 생성자 호출
  • 매개변수 있는 생성자를 호출할 수는 없음.
    - Circle circle Array[3](5); // 오류

배열소멸

  • 배열의 각 객체마다 소멸자를 호출합니다. 생성의 반대순으로 소멸합니다.

예제 4-2

#include <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();
    ~Circle();
};

Circle::Circle()
{
}
double Circle::getArea(){
    return 3.14*radius*radius;
}
Circle::~Circle()
{
}

int main(){
    Cricle circleArray[3]; // Circle 객체 배열 생성

    //배열의 각 원소 객체의 멤버 접근
    circleArray[0].setRadius(10);
    circleArray[1].setRadius(20);
    circleArray[2].setRadius(30);

    for (int i = 0; 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++;
    }
}#include <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();
    ~Circle();
};

Circle::Circle(/* args */)
{
}
double Circle::getArea(){
    return 3.14 * radius * radius;
}
Circle::~Circle()
{
}


int main(){
    Circle circleArray[3] ={
        Circle(10), Circle(20), Circle()
    };
    for (int i = 0; i < 3; i++)
    {
        cout << "Circle"<<i<<"의 면적은" << circleArray[i].getArea() << endl;
    }
    

    return 0;
}

객체 배열 초기화

  • 배열의 각 원소 객체당 생성자 지정하는 방법
    Circle circleArray[3] = { Circle(10), Circle(20), Circle()};
    - circleArray[0] 객체가 생성될 때, 생성자 circle(10) 호출
    - circleArray[1] 객체가 생성될 때, 생성자 circle(20) 호출
    - circleArray[2] 객체가 생성될 때, 생성자 circle() 호출

예제 4-3 객체 배열 초기화

#include <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();
    ~Circle();
};

Circle::Circle(/* args */)
{
}
double Circle::getArea(){
    return 3.14 * radius * radius;
}
Circle::~Circle()
{
}


int main(){
    Circle circleArray[3] ={
        Circle(10), Circle(20), Circle()
    };
    for (int i = 0; i < 3; i++)
    {
        cout << "Circle"<<i<<"의 면적은" << circleArray[i].getArea() << endl;
    }
    

    return 0;
}

정적할당

  • 변수 선언을 통해 필요한 메모리 할당
    - 많은 양의 메모리는 배열 선언을 통해 할당합니다.

동적할당

  • 필요한 양이 예측되지 않는 경우, 프로그램 작성시 할당 받을 수 없습니다.
  • 실행중에 힙 메모리에서 할당하면 됩니다.
    - 힙(heap)으로부터 할당
    • 힙은, OS가 프로그램의 실행을 시작 시킬 때 (즉, 프로그램이 메모리에 올라가{폰노이만구조} 프로세스가 될 때) 동적 할당 공간으로 준 메모리 공간입니다.
  • C언어에서 동적 메모리 할당 : malloc() / free()라이브러리 함수 사용
  • C++의 동적 메모리 할당 / 반환
    new 연산자
    - 기본 타입 메모리 할당, 배열 할당, 객체 할당, 객체 배열 할당
    - 객체의 동적 생성 : 힙 메모리로부터 객체를 위한 메모리 할당 요청

new && delete 연산자

  • C++의 기본 연산자

  • new / delete 연산자의 사용 형식
    데이터타입* 포인터 변수 = new 데이터타입;
    delete 포인터변수;

  • new / delete 의 사용

int* pInt = new int; // int 타입의 메모리 동적 할당
char* pChar = new char; // char 타입의 메모리 동적 할당
Circle *pCircle = new Circle(); // Circle클래스 타입의 메모리 동적 할당

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

예제 4-5, 정수형 공간의 동적 할당 및 반환 예

#include <iostream>
using namespace std;

int main(){
    int* p;

    p = new int; // int 타입 1개 할당함
    if(!p){ // p가 NULL이면, 메모리 할당 실패함.
        cout << "메모리를 할당할 수 없습니다."
        return 0;
    }
    *p=5;
    int n=*p;
    cout << "*p=" << *p <<'\n';
    cout << "n=" << n <<'\n';
    // 할당받은 메모리 반환

    delete p; 
}

delete 사용시 주의사항

  • 적절치 못한 포인터로 delete하면 실행시간에 오류가 발생합니다.

1) 동적으로 할당 받지 않는 메모리 반환 - 오류

int n;
int* p = &n;
delete p; // 실행시간 오류
// 포인터 p가 가리키는 메모리는 동적으로 할당 받은 것이 아님

2) 동일한 메모리 두 번 반환 - 오류

int* p =new int;
delete p;
delete p;

배열의 동적 할당 및 반환

new / delete 연산자 사용 형식
데이터타입 *포인터변수 = new 데이터타입[배열의크기]; // 동적 배열 할당
delete [] 포인터변수; // 배열 반환

4-6 정수형 배열의 동적 할당 및 반환

#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; // 배열 메모리 반환
}

예제 4-7, Circle 객체의 동적 생성 및 반환

#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; // 생성한 순서에 관계 없이 원하는 순서대로 delete 할 수 있음
}

참고로, 반환(delete)는 생성한 순서에 관계 없이 원하는 순서대로 delete 할 수 있음.

정수 반지름을 입력 받고, Circle 객체를 동적 생성하여 면적을 출력.

  • 음수가 입력되면 프로그램 종료

객체 배열의 동적 할당 및 반환

new / delete 연산자 사용 형식
클래스이름 *포인터변수 = new 클래스이름[배열의크기];
delete [] 포인터변수; // 포인터변수가 가리키는 객체 배열을 반환

this 포인터 (c++ 하려면, 거의 필수)

포인터, 객체 자신 포인터

클래스의 멤버 함수 내에서만 사용

개발자가 선언하는 변수가 아니고, 컴파일러가 선언한 변수임.

  • 멤버 함수에 컴파일러에 의해 묵시적으로 삽입 선언되는 매개변수입니다.
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가 필요한 이유

매개변수의 이름과 멤버변수의 이름이 같은 경우

멤버 함수가 객체 자신의 주소를 리턴할 때

  • 연산자 중복 시에 매우 필요함.

this의 제약사항

멤버 함수가 아닌 함수에서 this 사용 불가

  • 객체와의 관련성이 전혀 없기 때문입니다.

static 멤버 함수에서 this 사용 불가

  • 객체가 생기기 전에 static 함수 호출이 있을 수 있기 때문에

this 포인터의 실체 - 컴파일에서 처리함.

String 클래스를 이용한 문자열 (중요 !)

string 클래스

  • <string> 헤더 파일에 선언되어있음.
    우린 그냥 #include <string>해서 쓰면 됨 !!
  • 가변 크기의 문자열
string str = "I love "; // str은 'I', ' ', 'l', 'o', 'v', 'e', ' '의 7개 문자로 구성
str.append("C++."); // str은 "I love C++."이 된다. 11개의 문자
  • 다양한 문자열 연산을 실행하는 연산자와 멤버 함수 포함하고있음.
    - 문자열 복사, 문자열 비교, 문자열 길이 등
  • 문자열, 스트링, 문자열 객체, string 객체 등으로 혼용하여 사용합니다.

string 객체 생성 및 입출력

string 객체의 동적 생성

  • new / delete 를 이용하여 문자열을 동적 생성/반환 가능

예제 4-11, string 클래스를 이용한 문자열 생성 및 출력

#include <iostream>
#include <string> // string 클래스를 사용하기 위해선 반드시 필요합니다.
using namespace std;

int main() {
	// 스트링 생성
	string str; // 빈 문자열을 가진 스트링 객체 생성
	string address("서울시 성북구 삼선동 389");
	string copyAddress(address); // address의 문자열을 복사한 스트링 객체 생성

	char text[] = {'L', 'o', 'v', 'e', ' ', 'C', '+', '+', '\0'}; // C-스트링
	string title(text); // "Love C++" 문자열을 가진 스트링 객체 생성

	// 스트링 출력
	cout << str << endl; // 빈 스트링. 아무 값도 출력되지 않음
	cout << address << endl;
	cout << copyAddress << endl;
	cout << title << endl;
}

예제 4-12, string 배열 선언과 문자열 키 입력 응용

#include <iostream>
#include <string>
using namespace std;

int main() {
	string names[5]; // 문자열 배열 선언

	for(int i=0; i<5; i++) {
		cout << "이름 >> ";
		getline(cin, names[i], '\n');
	}
	
	string latter = names[0];
	for(int i=1; i<5; i++) {
		if(latter < names[i]) { // 사전 순으로 latter 문자열이 앞에 온다면
			latter = names[i]; // latter 문자열 변경
		}
	}
	cout << "사전에서 가장 뒤에 나오는 문자열은 " << latter << endl;
}

예제 4-13 문자열을 입력 받고, 회전

#include <iostream>
#include <string>
using namespace std;

int main() {
	string s;

	cout << "문자열을 입력하세요(한글 안됨) " << endl;
	getline(cin, s, '\n'); // 문자열 입력
	int len = s.length(); // 문자열의 길이

	for(int i=0; i<len; i++) {
		string first = s.substr(0,1); // 맨 앞의 문자 1개를 문자열로 분리
		string sub = s.substr(1, len-1); // 나머지 문자들을 문자열로 분리
		s = sub + first; // 두 문자열을 연결하여 새로운 문자열로 만듦
		cout << s << endl;
	}
}

문자열 find 및 replace

&가 입력될 때까지 여러 줄의 영문 문자열을 입력 받고, 찾는 문자열과 대치할 문자열을 각각 입력 받아 문자열을 변경하기

profile
정진, "어제보다 더 나은 오늘이 되자"

0개의 댓글