c++

20210721 함수 ~ 오퍼레이

TIN9 2021. 7. 21.
반응형

안녕하세요 틴구입니다

오늘은 함수 세부적인 내용이랑 오퍼레이터에까지의 내용을 담아보려고 합니다!!

갈수록 뇌가 과부하하는 거 같은 기분이네요 아직 2주밖에 안됐는데 ㅋㅋㅋㅋㅋㅋㅋ

그래도 한 번 끝까지 해보자구요!! 살아남아야지

바로 들어가볼까요~

우선 함수도 주소가 존재하기 때문에 해당 함수의 주소를 받아놓을 함수 포인터 변수를 선언하고 주소를

알고 있다면 함수 포인터 변수를 이용해서 해당 함수를 호출할 수 있습니다!

형태로는

반환타입 (*포인터명)(인자타입들);의 형태로 선언이 됩니다.

한번 예로 들어볼까요

#include <iostream>

ex)

void Output()

{

    std::cout << "Output" << std::endl;

}

void Output1()

{

    std::cout<< "Output1" << std::endl;

}

 

int main

{

    void (*pFunc)();;   // 반환타입 (*포인터명)(인자타입들);의 형태임

 

    pFunc = Output   // 본인이 설정한 포인터명에 Output함수를 대입

 

    pFunc();    // Output함수를 출력

 

    pFunc = Output1;    

 

    pFunc();

 

    return 0;

}

 

이렇게 출력을 하게 되면

Output

Output1

이 출력되게 된다.

인트 타입을 예로 들어 봅시다 (전처리기 생략)

ex)

// 인트 Number1, Number2의 값을 가져오게끔 생성

int Add(int Number1, int Number2)

{

    return Number1 + Number2;  // 가져온 값을 리턴 값으로 반환

}

 

int main()

{

    int (*pFunc1)(int, int) // 함수를 가져올 포인터 변수를 만들고

    

    pFunc1 = Add // 그 포인터 변수에 함수를 대입시킨다

 

    std::cout << pFunc1(50, 40) << std::endl;    // 출력하면서 인자 Number1, Number2 의 값에 숫자 50과 40을                                                                    // 넣어 + 후 90의 값이 반환되어 90이 출력되게 되는 것이다.

    return 0;

}

을 출력하게 되면 90이 출력되게 됩니다.

 

위의 내용을 배열로도 이용할 수 있습니다.

int Add(int Number1, int Number2)

{

    return Number1 + Number2;  // 가져온 값을 리턴 값으로 반환

}

 

int Minus(int Number1, int Number2)

{

    return Number1 - Number2;  // 가져온 값을 리턴 값으로 반환

}

 

int main()

{

    int (*pFunc2[2])(int, int)    // 함수 두 개를 사용하기 위해 배열 2개를 만들어준다.

    

    pFunc2[0] = Add      // 0번 인덱스에 Add함수

    pFunc2[1] = Minus   // 1번 인덱스에 Minus함수

 

    for (int i = 0; i < 2; ++i)

    {

        // 각각의 인덱스에 10, 20이란 값을 대입하여 계산 후 출력

        std::cout << pFunc2[i](10, 20) << std::endl;

    }

    return 0;

}

을 출력하게 되면

30

-10의 값이 나오게 됩니다.

레퍼런스

레퍼런스란 다른 변수를 참조하는 변수를 만드는 개념입니다

레퍼런스는 반드시 선언과 동시에 참조할 변수를 지정해주어야 합니다.

ex)

int Number = 100;

int& NumberRef; --- X

int& NumberRef = Number; --- O

그 이후에는 다른 변수를 넣더라도 참조가 아닌 해당 변수의 값을

레퍼런스가 참조하는 변수에 대입해주는 역할을 합니다.

ex)

int main()

{

    int Number = 100;

 

    int& NumberRef = Number;

 

    std::cout << Number <<std::endl;

 

    NumberRef = 9999;

 

    std::cout << Number <<std::endl;

 

    int Number1 = 500;

 

    NumberRef = Number1;

 

    std::cout << Number << std::endl;

 

    return 0;

}

을 출력하게 되면

100

9999

500이 출력하게 됩니다

위의 값을 분석해보면

int main()

{

    int Number = 100;

 

    int& NumberRef = Number;   // NumberRef 가 Number의 100 값을 참조하게 되어 100이 된다

 

    std::cout << Number <<std::endl;  // 100 출력

 

    NumberRef = 9999;   // NumberRef에 9999 정수를 대입하여 Number의 값이 9999로 바뀌게 된다

 

    std::cout << Number <<std::endl;    // 9999 출력

 

    int Number1 = 500;    // Number1의 변수에 임의의 값 500을 넣어준다

 

    NumberRef = Number1;     // NumberRef에 Number1의 값을 넣어주어도 레퍼런스는 그 값을 받아와

                                        // Number 변수에 값을 대입시켜주기만 한다.

    std::cout << Number << std::endl;     // 500 출력

 

    return 0;

}

이렇게 분석해 볼 수가 있습니다.

 

또 레퍼런스에서 중요한 부분이 있는데

구조체만의 변수 타입으로 변수를 만들고 해당 타입을 인자로 넣을 때의 경우입니다

 

// 구조체는 메모리 크기가 큰 구조체가 만들어질 수도 있다.
// 그렇기 때문에 단순하게 구조체를 인자로 이렇게 넘길 경우 구조체의 모든 멤버들을
// 복사하게 되는 상황이 발생할 수 있다.
// 이 경우 구조체의 크기가 크면 클수록 성능이 떨어지게 된다.
void TestPlayer(Player player)
{
}

// 구조체의 값을 변경하는 것이 아니라면 const Reference를 이용해서 
// 참조를 얻어오고 앞에 const가 붙어있기 때문에 대상의 값을 변경하는 것이 아닌
// 단순히 얻어오는 용도로만 사용할 수 있는데 당연히 참조를 하는 방식이기 때문에
// 새로운 player를 생성해서 복사하는 것이 아닌 참조를 하게 되는 것이다.
// 그러므로 몇백 바이트 몇천 바이트를 복사하게 될 경우 느려지게 될 텐데
// 단순 참조만 하게 되어 인자를 넘겨주는 속도가 향상될 수 있다.
void TestPlayer1(const Player& player)
{
    // player.Attack = 3030;
}

 

int main()

{

    Player  player = {};

    TestPlayer(player);    // 의 경우 구조체 전체의 값을 갖고 오는 형태

    TestPlayer1(player);  // 의 경우 구조체를 참조만 하여 값을 갖고오는 형태

 

    return 0;

}

 

위의 내용은 선생님께서 써주신 내용 그대로 복사해왔습니다 중요한 부분 같아서 괜히 안 건들고

이대로 숙지하는 게 좋을 거 같아요!!

 

동적할당

동적할당은 진짜 엄청 중요하다고 합니다!!

동적할당이란 미리 메모리를 생성하는 것이 아닌 내가 원하는 시점에 메모리를 생성해 줄 수 있는 기능입니다.

new, delete를 이용해서 동적할당에 관련된 기능들을 만들어 나갈 수 있습니다.

 

형태

new 생성하고자 하는 타입; 을 해주게 되면 해당 타입의 크기만큼을 힙 영억에

공간을 생성하고 생성된 공간의 메모리 주소를 반환합니다.

 

아래의 내용 부분이 엄청 중요합니다.

동적할당된 메모리는 자동으로 절대 안 지워집니다.

무조건 delete를 이용해서 해당 메모리를 제거해야 합니다.

만약, 동적할당을 해놓고 delete를 안 한다면 메모리 릭이 남게 됩니다.

메모리 릭은 동적할당하고 안 지우면 해당 메모리는 계속 사용되는 중이라고

인식되는 것을 말합니다.

 

delete 메모리 주소; 를 통해서 해당 주소를 제거할 수 있다.

 

new를 이용해서 생성하면 그 주소가 반환되기 때문에 이 주소를 제거해야 하는 것이다.

ex)

int main()

{

    int* pdNumber = new int; 

 

    *pdNumber = 9999; 

 

    delete pdNumber;

    

    return 0;

}

 

new int를 하면 힙 영역에 4바이트만큼 공간이 할당된다.
그 후에 해당 주소를 반환해주고
pdNumber는 지역변수이기 때문에 스택에 공간이 만들어지고
pdNumber가 힙영역에 공간이 만들어진 주소를 받아놓고
있는 것이다.
주소를 알기 때문에 해당 힙영역에 접근하여 값을 컨트롤하고 다 썼다면
해당 주소를 이용해서 delete로 메모리를 제거하는 것이다.라고 합니다!!

단 제거된 메모리 주소를 저장하는 포인터를 댕글링 포인터라고 하는데

만약 이 댕글링 포인터를 사용하려고 한다면 프로그램은 그 즉시 문제가 발생하여

중료 될 수 있으니 주의 바랍니다!.

위에서

delete pdNumber;

했는데 그 밑에 코드에서

*pdNumber = 555;

이렇게 사용이 불가능합니다.

 

또 배열도 마찬가지로 new를 사용해서 동적으로 만들 수 있습니다.

형태는

new 타입[개수];

로 이루어져 있습니다

 

// 인트 * 10 크기의 메모리가 힙 영역에 만들어지고 그 시작 주소를

// 반환해서 pArray 포인터 변수가 주소를 가지고 있게 된다.

int* pArray = new int[10];

 

for (int i = 0; i < 10; ++i)
    {
        pArray[i] = i + 1;
    }

delete[] pArray;

// 제거가 된 메모리 주소를 가지고 있는 포인터 변수는 항상
// nullptr로 초기화를 해주는 것이 좋다.
// nullptr은 0이다.
// 포인터는 다 사용을 했다면 항상 nullptr 로 초기화하는 습관이 필요하다.
pArray = nullptr;

operator

operator은 모든 연산자를 사용자가 원하는 대로 재정의 해서 사용할 수 있는 기능입니다.

 

구조체 혹은 나중에 배울 클래스에서 operator을 이용해서 원하는 연산자를 원하는 기능으로 만들어줄 수 있답니다!.

 

형태로는

반환타입 operator 재정할연산자(인자)

{

}

로 이루어져 있습니다.

 

struct Point
{
    int   x;
    int   y;

    Point operator + (const Point& pt)
    {
        Point   result;

        // 그냥 x를 쓰면 이 구조체의 변수를 사용하는 것인데
        // pt1 + pt2 를 했다면 x는 pt1의 x값이 된다.
        // x + pt.x 를 하게 될 경우 pt1의 x값 + 인자로 pt2가 들어왔으므로
        // pt2의 x값을 더한 것이 된다.
        // pt1.x + pt2.x 가 되어서 result.x 에 저장되는 것이다.
        result.x = x + pt.x;
        result.y = y + pt.y;

        return result;
    }

}

int main()

{

    Point pt1, pt2, pt3;

 

    pt1.x = 10;
    pt1.y = 20;
    pt2.x = 30;
    pt2.y = 40;

 

    // 아래에서 + 연산을 해주고 있는데 이 경우 pt1의 + operator를 호출한다는
    // 의미이다. pt1의 + operator를 호출할 때 뒤에 있는 pt2를 operator의
    // 인자로 넣어주는 것이다.
    pt3 = pt1 + pt2;

 

    std::cout << "x : " << pt3.x << " y : " << pt3.y << std::endl; 

 

    return 0;

}

 

x : 30 y : 60이 나오게 된다

이것을 활용해 다른 연산자도 사용이 가능하다

void operator += (const Point& pt)
    {
        x += pt.x;
        y += pt.y;
    }

 void operator ++ ()
    {
        ++x;
        ++y;
    }

 // ++을 뒤에 붙일 때는 아래와 같이 설정된 operator가 필요하다.
    void operator ++ (int)
    {
        x++;
        y++;
    }

등등이 사용 가능합니다

 

오늘은 진짜 중요한 부분이 엄청 많은 거 같아요... 달달 외워야 할 거 같아요 진짜 이론적인 내용이 엄청 많네요

레퍼런스 동적 할당 등등 다 엄청 사용 많이 한다고 하는데 큰일 났습니다 ㅋㅋㅋㅋㅋ

쨋든 오늘은 여기까지 하는 걸로 하고 다음에 봬요!!

반응형

댓글