C++ 이론(1)

Hi Beck·2023년 6월 21일

TIL-Language(C & C++)

목록 보기
27/34

1강

  1. 프로세스(빌드까지 한 파일을 실행한 것)한 시점의 메모리 형태

운영체제가 무조건 메모리를 빌려주는게 아니라 실행할 때 프로그램을 운영할 만큼의 메모리만 때줌.

이 메모리의 영역은 다음과 같이 나눠진다.
코드영역, 데이터 영역(전역 변수,STATIC 변수), 힙 영역(프로그래머 할당), 스택 영역(지역변수,매개변수)

ㄱ.스택영역: 임시 변수들 메모리가 잡히는 곳 (메모리 공간이 유동적임,임시적으로 생겼다가 사라져서)

프로그램이 실행되면 메모리 한덩이를 딱 때어줌

데이터의 끝에서부터 위로 영역이 생김

스택영역은 변수가 생길 때 바닥에서부터 차곡차곡 쌓아져서 올라가는데 삭제되는건 위에서부터 삭제가 된다.
(FILO OR LIFO)

함수가 끝나면 지역변수의 메모리 반납을 마지막 변수부터 선언 역순으로 한다는 소리임

ㄴ.데이터영역: 전역변수,스테틱변수들 메모리가 잡히는곳(이건 메모리가 정해져있음)

ㄷ.힙 영역: 동적할당 메모리가 쌓이는 것(추후할 예정)

메모리의 시작점부터 아래로 영역을 잡아감

스택 영역과 힙 영역이 각각 차서 만나게 된다는 것은 메모리가 꽉 찼단 것이고,그 시점 부터 프로그램이 돌아가지않는다.(관리 필요)

변수같은건 운영체제에 요청해서 받아오지만 동적할당같은건 어떤 필요에 의해서 우리가 직접 할당 받아오는 것이기에 위험할 수도 있다.

ㄹ.코드영역
우리가 IDE에서 작업했던 CPP파일이나 해더파일들

  1. 래퍼런스

&연산자에는 주소값을 계산해서 가져오는 기능이랑 또 하나의 기능이 있는데 그거게 바로 '래퍼런스'이다.

래퍼런스는 포인터 선언과 비슷하다.
아래처럼 &가 자료형과 변수명 사이에 있으면 얘는 래퍼런스 변수다라고 생각

iNUM = 10;
int& rRef = iNUM;

래퍼런스의 역할
== 오른쪽 변수에다가 rRef라는 새로운 이름(별명)을 지어주겠다.

기본적으로 iNUM은 메모리를 할당받아 선언이 되어있는데 rRef는 새로 생긴게 아니라 iNUM의 메모리를 공유하게된다.
-> 이름이 2개인 메모리 공간이 되는 것

rRef도 안에 10이 들어있다고 보면됨
(메모리가 별개가 아니라 진짜 본질은 하나이기에 공유한다는게 메모리 하나라는 소리다.rRef도 똑같은 값을 갖고있고 동일한 주소값을 갖는다.)

  • 래퍼런스를 쓰는 이유?
    C++를 사용하면서 체득이 됨
  1. 참조 대상 변경 불가
    참조 대상 == 래퍼런스가 대상으로 삼는 변수

래퍼런스는 선언할 때 동시에 무조건 초기화(대상:변수)까지 해줘야된다.

int& rRef; (X)
int& rRef = iNUM;(O)

래퍼런스는 누군가를 참조했으면 참조의 대상을 다시 바꿀 수는 없다.(빌드 에러남)

  1. 포인터와 래퍼런스의 차이

포인터,래퍼런스를 프로그래머마다 쓰는게 다르고 각 장단점이 있지만 C++은 래퍼런스를 적극적으로 쓴다.

ㄱ.차이점(변경 유무)
포인터는 대상을 미리 NULL로 정해놓고 나중에 바꾸는 것이 가능해진다.그러고 여러번 바꾸는것이 가능

-> NULL이 들어있는지 체크 필요

래퍼런스는 반드시 선언과 동시에 초기화를 하고 래퍼런스는 변경이 불가능함

-> 래퍼런스는 무조건 데이터값이 있는 상황의 코드에만 쓰여야됨.
케릭터들이 싸우는 도중 플레이어가 나간 경우의 NULL을 체크해야되는 상황인데 래퍼런스는 이 체크를 못함

ㄴ.차이점2(메모리 공간의 소모)
포인터 변수는 변수기 때문에 4바이트를 가져간다.

래퍼런스는 참조 대상의 메모리를 가짐으로서 메모리 소요가 없다.(원본 자체 데이터)

ㄷ.차이점3(호출시)
포인터의 경우
매개변수로 함수 인자 전달 시,메모리 소모(매개변수 갯수만큼)가 일어나고 값 복사가 발생된다.(값복사는 추후에)

값복사 : 포인터가 가리키는애의 값이 함수정의문으로 오면서 복사가 한번 되어야되는데

우리가 함수를 호출할 때 주의했던게 이름으로 매개변수를 받아서 함수정의문에서 코드를 짠 후 메인코드에서 호출하면 지역변수이기때문에 원하던 결과값을 볼 수 없었기에 매개변수를 주소값으로 넘겨받게끔 하고 포인터든 배열이름이든 인수로 받았는데

이 래퍼런스 변수는 이름으로 접근했는데도 불구하고 같은 메모리 그 자체이기 때문에 함수정의문에서 매개변수를 래퍼런스로 받으면 호출 시 지역변수 영향을 안받고 우리가 원하던 결과값으로 받을 수 있다.

  • 래퍼런스를 정의 및 초기화를 안하고 함수정의문의 매개변수를 래퍼런스 변수로 받은 경우?

함수정의문에 있는 래퍼런스 매개변수는 정의했을뿐이고 이게 실제로 생기진않았다. 함수라는것은 평소에는 메모리에 잡히지않다가 호출될 때 잡힌다.즉 호출할 때 래퍼런스 변수도 선언이 된다.

그러면 호출 인자로 (a,b)를 넘겼을 때 어떻게 선언이 되냐면 매개변수가
int& rRef X,int& rRef Y라면

int& rRef X = a;
int& rRef y = b;와 같아진다.
즉 호출 되는 순간 선언 및 초기화된거랑 같아짐.

a.포인터변수의 함수문 작동시 스택 영역 메모리 구조 변화 과정

지역변수의 메모리인 스택영역에서 메인에서 임의의 변수 하나를 선언하면 스텍 메모리에 변수 하나만큼 메모리가 밑에 쌓이고 또 다른 변수를 선언하면 또 위에 변수의 자료형만큼의 메모리가 쌓인다.(num1,num2)그리고 임의의 함수를 호출하면(매개변수가 포인터변수고, 인자가 주소값이 들어가야된다고 가정할때.ex - &num1, &num2 )

함수정의문의 매개변수에 주소값이 들어가면서 스택영역 변수메모리 위에 포인터변수의메모리가 쌓인다.(&num1, &num2에 대한 포인터변수 2개에 대한 메모리,현재까지 총 4번 쌓임)

  • 이때 스택 영역에는 함수메인 소속이든 함수정의문 소속이든 상관없이 메모리들이 붙어서 쌓인다.

그리고 함수정의문에 새로운 변수가 선언되고 초기화되면 그게 또 쌓임

그리고 대입 코드들이 있다면 이건 새로 쌓이는게 아니라 할당되었던 메모리 위치에서 정보만 바뀐다.

b.매개변수가 포인터 변수가 아닌 래퍼런스 변수일때의 함수 작동시 메모리 구조 변화 과정

위처럼 똑같이 메인에서 선언한 변수 2개가 메모리 구조에 두 번 쌓이고,함수정의문으로 가면 변수가 래퍼런스로 잡혀있기에 메모리구조에서 쌓여있던 변수 2개 메모리가 새롭게 쌓이는거없이 각각 래퍼런스의 별명으로 바뀌기만 한다.
그리고 함수정의문 내에서 선언된 변수만 추가가 됨

결론 : 전자의 경우 후자보다 포인터변수 2개로 인해 8바이트가 스택영역 메모리 구조에서 더 소모하게된다.

5.우리가 포인터나 래퍼런스를 쓰는 이유는 구조체나 배열 및 변수를 이 2가지 없이 쓴다고 가정했을때 , 매개변수를 일반 변수로 받으면 시간이 너무 오래걸리기때문이다.

예시로 매개변수를 구조체로 받는데 구조체 안에 큰 크기의 배열이 있으면 호출될때마다 스텍영역에는 몇 천번 몇 만번 메모리가 쌓일 것이다.(구조체 변수 선언시 배열크기만큼 쌓이고 함수 작동되면서 배열크기만큼 또 쌓이고)

결론: 메모리를 엄청나게 절약시켜준다.
(단,포인터는 8바이트가 소요,그래서 포인터도 많이 쓴다고한다.)

6.객체지향언어

객체(=물체,object)를 가지고 프로그래밍한다.

c++은 절차지향언어이면서 객체지향언어다.

구조체는 사용자 정의 자료형으로 우리는 이전까지 자료형을 가지고 변수를 만들었다.
즉,구조체로 구조체 변수를 생성할 수 있다는것이고, 그 말은 클래스로 클래스 변수를 생성하는게 가능하단 소리다.
클래스는 객체로,객체는 곧 구조체 변수다.

그렇다면 객체 안에 함수는 없는건가?

구조체나 클래스 자체가 자료형이기에 안에 함수가 있다없다를 논할 수 없다.

구조체를 가지고 구조체 변수를 만들었을때 그 변수 안에 멤버변수들이 존재하는것처럼 멤버함수들도 존재한다.그 함수들은 일반적으로 호출되는 함수들이 아니라 그 구조체 변수를 가지고 호출할 수 있는게 멤버함수다.

객체 즉 구조체 변수 안에는 멤버변수들이 있고 멤버함수들이 있다했는데,
객체의 멤버변수를 가져다가 사용할 수 있고,객체를 가지고 멤버함수를 호출할 수 있음.

그렇다면 C언어든 C++든 구조체나 클래스를 사용하고 변수를 사용하는게 같은데 C언어도 객체지향언어 아닌가?

-> C언어도 얼마든지 객체지향식으로 프로그래밍이 가능

결국은 객체지향언어는 컨셉이다. C언어에 일부 추가된 부분이 방대한데 그게 객체 지향 중심으로된 문법이기때문이다.편의를 위한 몇 가지 문법이 추가된것

7.객체의 구조
class 변수명{
private:
//멤버변수들

public:
//멤버함수들

};

여기서 알아야될건 멤버변수들의 생존주기는 가장 큰 중괄호 안인데 그말은 멤버함수안에서 컨트롤이 가능하다.
(읽어들이고 값을 변경할 수 있음)

외부에서는 멤버변수들을 가져다쓸 수도 있고 없을 수도 있다.(==컨트롤할 수 있다.)

우리가 구조체에서 c.name 이런식으로 했던 것처럼 외부에서 참조하는식으로 가능함

8.객체 멤버함수 호출 코드문 예시

class MYcar {
private:
int fuel = 0;
bool power = false;

public:
void go() {
this -> fuel--;
}

void oiling(int n) {
this -> fuel += n;
}
void fuel_check() {
std::cout << "연료 : " << fuel << std::endl;
}
};

int main (){
MyCar car = MyCar(); //객체(클래스 변수) 선언(객체 생성)

//함수 호출
car.oiling(100);
car.fuel_check();
for(int i = 0; i < 10: i++) car.go();
car.fuel_check();
car.oiling(100);
for(int i = 0; i < 10: i++) car.go();
car.fuel_check();

};

위를 보면 객체 안의 멤버함수를 메인에서 호출할 수 있다.

객체이름.함수이름(인자);

이런식으로 객체이름 안에 있다는것을 명시해주는게 중요하다.

또한 oiling 호출한 순간 멤버 변수 fuel이 변한걸 볼 수 있다.
멤버변수의 값을 변경시키는 방법으로
우리가 멤버변수의 값을 직접적으로 변경시킬 수도 있는 방법이 있고,

ex) car.fuel + 100;

함수를 통해 값을 변경시킬 수도 있다.
-> 직접 바꿀 수 있는데 굳이 이렇게 하는 이유는 장점이 있는데 뒤에 나옴

정리하자면 객체의 상태(멤버 변수, 함수)를 계속 변화시키고 컨트롤하면서
객체의 상태변화를 화면에 띄어주고 그 프로그램을 전개하는게 객체지향언어다.

예시) 게임의 몬스터는 똑같은게 아니라 다 객체 중 하나다.관련된 특성,행동 다 클래스 하나로 묶어서 관리한다.

  1. 접근제어자

접근을 제어해주는 역할을 하는건데, 외부에서 접근을 해주게하냐 마냐의 차이다.

외부에서 접근을 가능하게끔 제어해주는게 '접근제어자'다.

종류로는 private(외부에 공개X),public(외부에 공개o),protected(뒤에서 상속배울때)
등이 있다.

처음은 private부터 나오면서 다른 접근제어자가 나올때까진 계속 프라이빗이다.
객체의 디폴트 접근제어자는 private다.그래서 안쓰면 전체가 private가 된다.

C언어에서 배웠던 구조체는 기본적으로 public이다.

프라이빗 안에 있는 요소들(fuel같은거)은 메인함수 즉 외부에서 쓰일 때 빌드에러가 나는데 프로그램이 실행될려면 필요한데 왜 막을까? 그리고 이걸 쓸려면 어떻게 해야될까?

car.oiling(100);처럼 함수호출로 우회해서 사용한다.(oiling은 퍼블릭에서 속하고 밖에서 호출할 수 있음.그리고 oiling 안에서 fuel을 바꿔주고있다.프라이빗이지만 퍼블릭 내부에 있기때문에 건드릴 수 있음

  1. 프라이빗의 존재 이유
    1.협업 문제
    되도록이면 건들고싶지않은 것들이 있을때 쓰는데, 사람이 잘안건드릴려고해도 협업하다보면 건드리게 되어있기때문이다.(중간에 누군가 뭘 껴놓으면 그걸 되게 찾기힘들다.)

2.보안문제
코드는 유추하기 어렵지만 숫자는 유추하기쉬워서 이런걸 해커들이 건드린다.

  1. 매개함수 장점
    예외처리란게 있는데 함수안에서 조건식을 써서 수치를 어느 기준 범위안에서만 처리하고 나머지는 예외로 처리하는 것이다.(비정상동작이든 해킹이든 개발자 실수든 잡을 수 있음)

결론 민감한건 개발할 때 프라이빗 안에 포함시키고 마지못해 퍼블릭에 있어도 매개함수 우회하는 식으로 한번 감싸서 코드를 짜자.

profile
Emotional realizer

0개의 댓글