728x90

c++은 객체지향 프로그래밍

- 컴파일 시간이 아닌 '실행 시간'에 어떠한 '결정'을 내릴 수 있다.

 

e.g.) 배열을 생성할 때,

재래적 절자척 프로그래밍: 배열의 크기를 미리 결정해야 함

객체지향 프로그래밍: 배열의 크기를 실행 시간에 결정할 수 있음-> 값이 변할 수 있는 변수배열의 크기를 결정할 수 있음

 

일주일에 한 번만 200개의 공간이 필요하다면, 재래적 절차적 프로그래밍의 경우 남은 6일 간 180이 낭비됨

객체지향 프로그래밍에서는, 20을 유지하다가, 필요에 따라 간헐적으로 200으로 늘림


포인터

변수 선언 과정에서, 변수의 이름을 통해 변수를 관리하는 것이 아니라, 저장할 데이터를 다루는 '주소'이름을 붙임

즉, 포인터는 포인터의 이름 자체주소를 나타냄.

이를 간접값 연산자, 간접 참조 연산자 * 로 나타냄

 

1-1. 포인터 선언, 변수와 연결

#include <iostream>

using namespace std;

int main(){

    int val = 3;
    cout << &val << endl; //val이라는 변수가 저장된 메모리 주소 : 0x16b0135fc

    /*포인터 선언하기*/
    //포인터는 개념적으로 정수와 다른 개념 (수가 아닌 위치를 나타냄)
    //포인터에는 어떤 정수를 대입하는 것이 아무런 의미가 없음
    //대신, 포인터에 어떠한 변수의 주소를 대입함으로써,
    // 포인터를 이용하여 해당 주소의 값에 직접 변화를 줄 수 있음

    int *a; // c style
    int* b; // c++ style
    int* c,d; //c는 포인터 변수, d는 int형 변수로 선언됨
	
    /*e의 주소 저장을 위해 f가 선언됨*/
    int e = 6;
    int* f; //그 자체로 주소를 나타냄

    f = &e; //포인터 f와 변수 e가 연결됨 -> f가 바뀌면 e도 연동되어 바뀜
    //위 코드는 *f = e;로 대체할 수 없음(error)

    cout << "e의 값 " << e << endl; //e의 값 6
    cout << "*f의 값 " << *f << endl; //*f의 값 6
    cout << "e의 주소 " << &e << endl; //e의 주소 0x16fcf75d8
    cout << "*f의 주소 " << &*f << endl; //*f의 주소 0x16fcf75d8
    cout << f << endl; //0x16f7e35d8

    *f = *f + 1; //주소에 해당하는 값에 직접 변화 주기
    cout << "이제 e의 값은 " << e << endl; //이제 e의 값은 7
    cout << *f << endl; // 0x16d2ef5d8 -> 값이 저장된 주소는 바뀌지 않음

}

- 즉, 포인터는 특정 데이터의 '주소'저장을 위한 용도로 생성됨.

 

1-2. new를 통해 포인터 선언 및 초기화

#include <iostream>

using namespace std;

int main(){

    /*이전시간에 활용한 방법: 주소에 접근할 때 a와 b 둘을 통해 모두 접근 가능*/
    int a;
    int* b = &a;

    //new 연산자
    /*
     * 어떤 데이터형을 원하는지 new 연산자에 알려주면,
     * new 연산자는 '그'에 알맞은 크기의 메모리 블록을 찾아내고
     * 그 블록의 '주소'를 리턴함*/

    int* pointer = new int; //int형을 위한 새로운 메모리가 필요함을 알려줌
    //new는 int임을 확인하여 몇 바이트가 필요한지 계산(e.g. 4btyes)
    //해당 바이트를 저장할 수 있는 메모리 블록을 찾아 해당 주소를 반환
    
 
}

1-3. 배열 형식의 포인터 선언 및 초기화

#include <iostream>

using namespace std;

int main(){
    
    double* p3 = new double[3]; //double형 데이터 3개를 저장할 수 있는 공간을 대입
    p3[0] = 0.2; //p3를 배열 이름처럼 취급
    p3[1] = 0.5;
    p3[2] = 0.8;

    cout << "p3[1] is " << p3[1] << ".\n"; //p3[1] is 0.5.

    p3 = p3 + 1; //배열에 값을 더하는 행위
    //원래 배열 이름은 값을 변경할 수 없음.
    //그러나 포인터는 변수처럼 사용할 수 있어서, 값 변경 가능.
    /* double 데이터 3개를 저장하는 공간이므로,
     p3에 1을 더한다는 것 =뒤에 있던 double형 데이터 공간 1개를 앞당기는 것
     p3[0] -> 0.5를 point
     p3[1] -> 0.8을 point
     */

    cout << "Now p3[0] is " << p3[0] << " and "; //Now p3[0] is 0.5 //기존 p3[1]의 값
    cout << "p3[1] is " << p3[1] << ".\n"; // p3[1] is 0.8.


}

 

 

2. 포인터를 delete

- delete 연산: 사용한 메모리를 다시 메모리 풀의 영역으로 환수 => 환수된 메모리는 프로그램의 다른 부분이 다수 사용할 수 있음
- delete ps; //new를 사용한 다음에는 반드시 delete를 사용해야 함
- delete 없으면 ; 대입은 되었지만 사용되지 않는 메모리 누수 발생 -> 프로그램 먹통

#include <iostream>

using namespace std;

int main(){

    /*delete 연산: 사용한 메모리를 다시 메모리 풀의 영역으로 환수*/
    //환수된 메모리는 프로그램의 다른 부분이 다수 사용할 수 있음

    int* ps = new int;
    //포인터변수 ps를 선언, 메모리 사용
    delete ps; //new를 사용한 다음에는 반드시 delete를 사용해야 함
    //delete 없으면 ; 대입은 되었지만 사용되지 않는 메모리 누수 발생 -> 프로그램 먹통

    /*delete를 사용하는 네 가지 규칙*/
    //1. new로 대입하지 않은 메모리는 delete로 해제할 수 없음
    //2. 같은 메모리 블록을 연달아 두 번 연속 delete로 해제할 수 없음 (한번으로 충분)
    //3. new[]와 같이 대괄호 형식 메모리 대입할 시, delete 역시 delete[]로 해제
    //4. 대괄호를 사용하지 않았다면, delete

    double* p3 = new double[3]; //double형 데이터 3개를 저장할 수 있는 공간을 대입
    p3[0] = 0.2; //p3를 배열 이름처럼 취급
    p3[1] = 0.5;
    p3[2] = 0.8;

    cout << "p3[1] is " << p3[1] << ".\n"; //p3[1] is 0.5.

    p3 = p3 + 1; //배열에 값을 더하는 행위
    //원래 배열 이름은 값을 변경할 수 없음.
    //그러나 포인터는 변수처럼 사용할 수 있어서, 값 변경 가능.
    /* double 데이터 3개를 저장하는 공간이므로,
     p3에 1을 더한다는 것 =뒤에 있던 double형 데이터 공간 1개를 앞당기는 것
     p3[0] -> 0.5를 point
     p3[1] -> 0.8을 point
     */

    cout << "Now p3[0] is " << p3[0] << " and "; //Now p3[0] is 0.5 //기존 p3[1]의 값
    cout << "p3[1] is " << p3[1] << ".\n"; // p3[1] is 0.8.

    p3 = p3 -1;
    delete[] p3;
}

reference: https://inf.run/4ttD

728x90

+ Recent posts