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개의 댓글