📌 힙(heap)으로 부터 할당
힙은 운영체제가 프로세스의 실행을 시작 할때 동적 할당 공간으로 준 메모리 공간
👉 new 연산자
기본 타입 메모리 할당, 배열 할당, 객체 할당, 객체 배열 할당
객체의 동적 생성 - 힙 메로리로 부터 객체를 이한 메모리 할당 요청
객체 할당 시 생성자 호출
👉 delete 연산자
new로 할당 받은 메모리 반환
객체의 동적 소멸 - 소멸자 호출 뒤 객체를 힙에 반환
🍙 참고 :
CPU는 코드 영역에 저장된 명령어를 하나씩 가져가서 처리하게 됩니다.
데이터 영역은 프로그램의 시작과 함께 할당되며, 프로그램이 종료되면 소멸합니다.
스택 영역은 함수의 호출과 함께 할당되며, 함수의 호출이 완료되면 소멸합니다.
이렇게 스택 영역에 저장되는 함수의 호출 정보를 스택 프레임(stack frame)이라고 합니다.
스택 영역은 푸시(push) 동작으로 데이터를 저장하고, 팝(pop) 동작으로 데이터를 인출합니다.
이러한 스택은 후입선출(LIFO, Last-In First-Out) 방식에 따라 동작하므로, 가장 늦게 저장된 데이터가 가장 먼저 인출됩니다.
스택 영역은 메모리의 높은 주소에서 낮은 주소의 방향으로 할당됩니다.
힙 영역은 사용자에 의해 메모리 공간이 동적으로 할당되고 해제됩니다.
힙 영역은 메모리의 낮은 주소에서 높은 주소의 방향으로 할당됩니다.
출처 및 참고 : https://junghyun100.github.io/%ED%9E%99-%EC%8A%A4%ED%83%9D%EC%B0%A8%EC%9D%B4%EC%A0%90/
#include <iostream>
using namespace std;
int main(){
//포인터 변수 선언
int *p;
// 포인터 변수에 new int 대입
// 포인터 변수에는 주소만 담을 수 있으므로 new를 통한 객체 생성 시 주소를 반환해준다는 의미
p = new int;
//메모리 할당을 실패할 경우 null을 반환, null => false / !로 반전을 주어 null 일 경우 메세지 출력 및 종료
if(!p){
cout << "메모리 할당 불가!!";
return 0;
}
//p : 동적 할당 받은 주소 , *p : 동적 할당 받은 주소의 값
*p = 5;
// n에 포인터변수의 값을 담음
int n = *p;
cout << *p << endl;
cout << n << endl;
//할당 받은 메모리 반환
delete p;
}
결과
5
5
int n;
int *p = &n;
delete p;//실행 시간 오류
//포인터 p가 가리키는 메모리는 정적할당 받은 메모리 공간
//-------------별개
int *p = new int;
delete p;
delete p; 실행 시간 오류. 이미 반환한 메모리를 중복 반환 할 수 없음
#include <iostream>
using namespace std;
int main(){
int n;
// n 변수에 배열 길이 입력 받기
cin >> n;
// n이 0보다 작으면 프로그램 종료
if(n <= 0) return;
//포인터 변수에 int형 n개의 배열 생성
int *p = new int[n];
//메모리 할당에 실패 시 프로그램 종료
if(!p){
return 0;
}
//배열 순차 Scan을 통해 각 요소 입력 받기
for(int i=0;i<n;i++)
cin >> p[i];
int sum = 0;
//각 요소를 순차 Scan하며 누산하기
for(int i=0;i<n;i++)
sum += p[i];
//평균 출력
cout << "평균" << sum/n << endl;
//배열 반환
delete p[]
}
//20으로 초기화된 int 타입 할당
int *pInt = new int(20);
//a로 초기화 된 char 타입 할당
char *pChar = new char('a');
int *pArry = new int[10](20); //구문 오류. 컴파일 오류 발생
int *pArry = new int(20)[10]; //구문 오류. 컴파일 오류 발생
int *p = new int[10];
delete p; // delete p[]
int *q = new int;
delete [] q; // delete q;
클래스이름 *포인터변수 = new 클래스이름;
클래스이름 *포인터변수 = new 클래스이름(생성자 매개변수 리스트);
delete 포인터변수;
#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;
// 객체 생성 : 인자 1를 가진 생성자 사용
q = new Circle(30);
// 포인터 변수를 사용한 멤버 접근으로 -> 사용
cout << p ->getArea() << endl << q -> getArea() <<endl;
//생성 순서와 관계 없이 원하는 순서대로 delete 할 수 있음
delete p;
delete q;
}
실행 결과
생성자 실행 radius = 1
생성자 실행 radius = 30
3.14
2826
소멸자 실행 radius = 1
소멸자 실행 radius = 30
클래스이름 *포인터변수 = new 클래스이름 [배열크기];
delete [] 포인터변수;
//3개의 Circle 객체 배열의 동적 생성
Circle *pArry = new Circle[3];
//Radius Setting
pArry[0].setRadius(10);
pArry[1].setRadius(20);
pArry[2].setRadius(30);
//각각 객체의 getArea() 함수 호출
for(int i = 0; i < 3 ; i++){
cout << pArry[i].getArea();
}
pArry -> setRadius(10);
(pArry+1) -> setRadius(20);
(pArry+2) -> setRadius(30);
for(int i = 0; i < 3 ; i++){
cout << (pArry+i)->getArea();
}
//배열 소멸 시 각 원소의 객체 소멸자 별도 실행, 생성의 반대순
delete [] pArray;
class Circle{
int radius;
public:
// this는 객체 자신의 포인터!! 이므로 -> 를 통하여 멤버에 접근
Circle(){ this -> radius = 1;}
Circle(int radius) { this-> radius = radius; }
void setRadius(int radius) { this -> radius = radius; }
}
연산자 중복 시 매우 필요
class Sample{
public:
Sample* f(){
...
return this;
}
}