C++ 정리

2022. 1. 6. 21:49C&C++

파라미터 디폴트값 : 함수의 파라미터에 디폴트값을 지정할 수 있다.

함수 호출 시점에 인자가 없다면 컴파일러가 넣어주는 원리.

선언 부분에서만 디폴트값을 지정할 수 있다. (선언과 정의를 나누지 않는다면 상관없음.)

디폴트값은 뒤의 인자부터 채워야한다.

 

static: static으로 선언한 정적변수는 블럭 안에서만 유효한 값을 가지지만 자동변수(지역변수)처럼 없어지지 않고, 블럭으로 다시 돌아왔을 때 이전 값을 사용할 수 있다. 프로그램과 생명주기를 같이 한다.

전역 static: 해당 소스 파일 내에서만 사용할 수 있고, 다른 파일에서 참조가 불가능해진다.

지역 static: 해당 지역 내에서만 사용할 수 있으며, 블럭을 나가는 것과 관계없이 값이 유지된다.

 

extern: 전역으로 선언하며, 다른 파일에서 해당 변수를 사용할 수 있게한다. 보통 헤더파일에서 선언하여 cpp파일에 해당 이름의 변수가 있음을 알린다.

(메모리 상에 해당 변수가 존재함을 알린다.)

 

네임스페이스 : 여러 라이브러리 / 여러 파일들을 같이 컴파일하다보면 함수 / 변수의 이름이 겹칠 수 있다.

네임스페이스는 말그대로 영역에 이름을 붙여서 같은 이름의 다른 함수 / 변수를 구분짓기 편하게 해준다.

 

레퍼런스(&) : 실제로 포인터와 동일하게 작동한다. 마치 일반 변수의 형태로 포인터처럼 쓸 수 있는 기능.

 

구조체 : C++의 구조체는 내부에 함수가 존재할 수 있다.

유일한 차이는 기본 접근 제어자가 구조체는 public, 클래스는 private이라는 것.

그럼에도 구조체는 C++에서 데이터 덩어리의 역할로 사용한다. 예) x,y,z 좌표의 지점

 

클래스 : 객체의 틀을 정의하는 것.

- 주로 선언은 헤더파일에, 정의는 cpp파일에 한다. (인라인함수는 선언에서 정의해야한다.)

- public 변수, Getter/Setter의 사용은 최대한 안해야 한다. (객체지향에 어긋남) -> 불가피한 경우 friend 처리

- const 함수를 습관화하는 것이 좋다.

- private, protected, public 접근제어자를 이용하여 객체의 멤버변수 / 멤버함수의 접근을 제어할 수 있다.

 

new, delete : 생성자 / 소멸자 외에는 malloc, free와 차이가 없다.

소멸자 있는 객체 배열을 new 할당하면 배열의 바로 앞 공간 4Byte에 배열 요소 개수가 저장된다.

 

객체 생성

- 생성자에서 멤버변수를 상수로 초기화하는 것보다 선언 시에 바로 초기화하는 것이 낫다.

- 객체를 생성자의 인자로 받아서 멤버 객체에 대입하는 경우에, 이니셜라이저를 통해 초기화하는 것이 성능에 좋다.

이니셜라이저는 복사 생성자를 호출하지만 (선언과 동시에 초기화),

생성자 내에서 대입할 경우에는 기본 생성자와 대입 연산자가 호출되기 때문.(선언과 대입의 분리)

또한, const 변수나 참조 변수(&)는 이니셜라이저를 통해서만 초기화할 수 있다.

- 객체를 동적할당할 경우에, 생성과 소멸을 동일한 블록이나 틀 안에서 책임지고 처리하는 것이 좋다.

 

객체 소멸

- 소멸자를 통해 객체가 delete될 때 수행되는 절차를 작성할 수 있다.

 

delete와 delete[]의 차이:

소멸자가 있는 객체의 배열이 아니라면, delete와 delete[]는 같은 동작을 한다.

하지만 소멸자가 있는 객체의 배열이라면, delete시에 순차적으로 소멸자를 호출시켜야하기 때문에,

new[]를 할 때 객체 배열의 바로 앞 4바이트에 배열의 길이 정보가 저장된다.

이 객체 배열에 대해 그냥 delete를 하면 소멸자가 호출되지 않기 때문에 메모리 누수가 발생할 수 있다.

 

멤버 함수의 내부 동작

- 멤버 함수 호출시에, this 포인터를 ecx 레지스터에 넣어서 전달한다.

- 멤버 함수에서 멤버 변수 접근시에 이 this 포인터 기준으로 접근한다.

- 객체 생성시에 메모리에 해당 객체의 변수들이 저장되지만, 멤버함수는 별개로 저장되지 않는다.

- 멤버함수는 객체들이 공용으로 접근한다. (물론 그 안의 멤버변수는 객체마다 각각 소유)

 

얕은 복사, 깊은 복사

객체에 객체를 대입할 때, 기본적으로 멤버 변수를 복사하는 기본 복사 생성자가 호출된다. (얕은 복사)

이 때 멤버 변수 중 포인터나 문자열의 경우에 주소값이 그대로 대입되는 문제가 생긴다.

이런 문제를 해결하기 위해서 복사 생성자를 따로 정의해주고 포인터같은 경우는 바로 대입하는 것이 아니라,

새로 동적으로 생성해주는 등 그 클래스에 맞게 복사 규칙을 만들어주는 것을 깊은 복사라고 한다.

 

상속

- 상속 시 생성자는 상속 계층의 상위부터 호출된다.

- 소멸자는 객체 자신부터 역순으로 호출된다.

- public / protected / private 상속이 있는데, 주로 public 상속을 한다.

(상속 시 기초 클래스의 private 멤버는 유도 클래스에서 접근 불가, protected와 public 멤버는 동일하게 유지)

- 상속은 코드의 재사용보다는 가상함수를 통한 다형성을 위해 사용해야한다.

 

가상 함수

- 부모(기초) 클래스의 함수에 virtual 키워드를 붙이는 방식으로 사용한다.

- 해당 함수를 유도 클래스에서 오버라이딩해서, 실제 객체의 오버라이딩된 함수를 사용할 수 있다.

ex) 기초 클래스 A와 이를 상속한 유도 클래스 B.

A클래스에 func라는 가상함수가 존재하고 B에서 이를 오버라이딩 했을 때,

A * b = new B;
b.func();

이 때 b객체는 A 타입의 객체지만, B 클래스의 func 함수를 호출할 수 있다.

 

- 가상함수의 원리

virtual 함수를 상속받은 유도클래스의 객체 구조는 아래와 같다.

가상함수 테이블 주소 ( _vfptr )
기초 클래스의 멤버 변수들 (val)
유도 클래스의 멤버 변수들 (a)

프로세스가 가동될 때, 위의 _vfptr과 같은, 특정 클래스에 대한 virtual 클래스 테이블들이 메모리에 올라간다.

(virtual 함수를 포함한 클래스 수만큼)

단, 이때 순수가상함수의 클래스 테이블은 메모리에 올라가지 않는다. (객체 생성이 되지 않으니까 그런 것 같다.)

 

 

'C&C++' 카테고리의 다른 글

[C++] 템플릿  (0) 2022.01.07
[C++] 형 변환 연산  (0) 2022.01.07
비트맵(.bmp) 파일 구조  (0) 2022.01.06
[C/C++] 파일 패킹  (0) 2022.01.06
[C/C++] 파일 입출력  (0) 2022.01.06