안녕하세요 틴구입니다
오늘은 함수 세부적인 내용이랑 오퍼레이터에까지의 내용을 담아보려고 합니다!!
갈수록 뇌가 과부하하는 거 같은 기분이네요 아직 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++;
}
등등이 사용 가능합니다
오늘은 진짜 중요한 부분이 엄청 많은 거 같아요... 달달 외워야 할 거 같아요 진짜 이론적인 내용이 엄청 많네요
레퍼런스 동적 할당 등등 다 엄청 사용 많이 한다고 하는데 큰일 났습니다 ㅋㅋㅋㅋㅋ
쨋든 오늘은 여기까지 하는 걸로 하고 다음에 봬요!!
'c++' 카테고리의 다른 글
20210723 사용자 정의 헤더 파일 ~ Template (0) | 2021.07.25 |
---|---|
20210722 객체 지향 프로그래밍 ~ static멤버함수,싱글톤 (0) | 2021.07.23 |
20210720 쌤 빙고 코드 분석 ~ 함수의 나머지 부분 (0) | 2021.07.21 |
20210719 포인터,함수 ~ 과제 (0) | 2021.07.20 |
20210716 구조체 ~ 포인터1 (0) | 2021.07.17 |
댓글