객체 지향은 변수와 함수들의 모임인 객체 단위로 보는 것을 말합니다. 특징으로는 추상화, 캡슐화, 상속, 다형성이 있습니다.
추상화는 필요로 하는 속성, 행동을 추출하는 것 입니다.
캡슐화는 낮은 결합도를 유지하도록 설계하는 것을 말하며 정보 은닉을 할 수 있습니다.
상속은 공통된 특성을 하나의 개념으로 정립하는 것을 말합니다.
다형성은 다른 클래스의 객체가 같은 메시지를 받았을 때 각자 방식으로 반환하는 것을 말합니다.
(hash table = unordered_map)
해시테이블의 값은 키에 대한 해시 함수를 호출하여 저장합니다. 값은 정렬된 순서로 보관되지 않으며 값을 저장한 인덱스를 찾기 위해 키를 사용합니다. 삽입 및 검색은 분할상환적으로 O(1) 시간에 수행됩니다. 또한, 잠재적 충돌을 고려하여 구현해야 합니다. 보통은 충돌되는 값을 서로 체이닝하여 이 문제를 해결하는데, 특정 키에 대응되는 인덱스 위치에 모든 값을 연결리스트로 묶는 방법으로 해결합니다.
Map은 키를 기준으로 만든 red-black tree에 키와 값 쌍으로 보관합니다. 충돌을 처리할 필요가 없으며 트리의 균형이 유지되므로 삽입, 검색 연산의 시간은 O(logN)으로 보장됩니다.
new 키워드를 사용해 동적으로 할당받은 메모리는 반드시 delete 키워드를 사용해 해제해 줘야 합니다. 하지만 개발자의 실수나 잘못된 설계로 인하여 해제가 안되는 경우가 많습니다. 이러한 경우를 방지하고 안전한 사용을 위해 등장하게 된 것이 스마트 포인터 입니다.
unique_ptr는 하나의 스마트 포인터만이 특정 객체를 소유할 수 있도록 객체에 소유권 개념을 도입한 스마트 포인터 입니다. 해당 객체의 소유권을 갖고 있을 경우에만 소멸자가 해당 객체를 삭제할 수 있습니다.
shared_ptr는 하나의 특정 객체를 참조하는 스마트 포인터가 총 몇개 인지를 참조하는 스마트 포인터입니다. 참조 횟수가 0이 되면 delete 키워드를 사용해 자동으로 해제됩니다.
오버로딩은 메소드의 이름은 같지만 매개변수만 다르다면 정의하고 사용할 수 있습니다. 오버라이딩은 상속 관계에서 클래스 간 같은 이름을 가진 메소드를 정의하는 방법입니다. 부모 클래스에서 선언된 메서드를 자식클래스에서 재정의하여 사용하는 것을 말합니다.
오버로딩은 메소드 이름만 같고 나머지는 달라야 하며, 오버라이딩은 이름, 매개변수와 그 타입들, 반환 타입이 같아야 합니다.
가상함수는 가상 테이블에 의존합니다. 어떤 클래스의 함수가 virtual
로 선언되있으면, 해당 클래스의 가상 함수 주소를 보관하는 고유한 가상 테이블이 생성됩니다. 이 포인터는 가상테이블의 시작주소를 가리키는 포인터입니다.
하위 클래스가 상위 클래스의 가상 함수를 오버라이드하지 않으면 하위 클래스의 가상 테이블은 상위 클래스의 가상 함수 주소를 보관합니다.
이 가상테이블을 사용해 가상 함수가 호출될 때 어느 주소에 있는 함수가 호출되어야 하는지를 결정합니다.
소스 코드 작성, 전처리, 컴파일, 어셈블, 링크, 실행 순으로 수행됩니다. 먼저, 사용자가 작성한 소스코드의 #으로 시작하는 전처리기 구문을 처리합니다. 그 다음 고수준의 언어를 어셈블리 언어로 번역하는 작업을 합니다. 어셈블 단계에서는 어셈블리 파일을 오브젝트 파일로 만들고 오브젝트 파일을 합치고, 링크 단계에서 라이브러리와 연결해 주고 실행 가능한 파일을 생성해줍니다.
얕은 복사는 한 객체의 모든 멤버 변수의 값을 다른 객체로 복사합니다. 깊은 복사는 그 외에 포인터 변수가 가리키는 모든 객체에 대해서도 시행합니다.
배열은 변수 선언과 함께 자신의 데이터를 저장할 공간을 배열의 크기만큼 연속적인 공간을 가집니다. 포인터는 자신이 데이터를 저장할 공간을 갖지 않고 저장할 공간이 있는 위치를 저장하는 메모리 번지를 저장합니다.
포인터와 배열은 둘다 변수 자체는 메모리 번지를 뜻하지만, 배열은 포인터 상수여서 다른 번지를 가리킬 수 없고, 포인터는 변수이므로 대입이 가능합니다.
포인터 변수는 증감 연산자가 사용가능하지만, 배열은 상수이므로 사용할 수 없습니다.
바인딩은 함수가 호출될 때 함수의 주소를 연결해 주는 것을 말합니다.
정적 바인딩은 컴파일 시점에 일어나고 프로그램 실행 과정에서 변하지 않고 유지되는 바인딩이며, 컴파일 시 결정되므로 속도가 빠르고 에러를 조기에 발견할 수 있다는 장점이 있습니다. 단점으로는 컴파일 이후 변경이 불가능하다는 점이 있습니다.
동적 바인딩은 런타임 중 결정되며 실행 과정에서 변경되는 바인딩을 말합니다. 가상 함수 호출시 오버라이딩된 가상 함수를 동적으로 찾아 호출할 때 사용하며, 런타임 중 자유롭게 바뀔 수 있어 적응성이 높지만, 타입 체크로 수행 속도 저하가 발생할 수 있습니다.
동적 바인딩을 사용하는 이유는 어떤 포인터에 의해 접근되었는지 상관없이 참조된 인스턴스의 실제 클래스에 따라 재정의 된 함수를 호출할 수 있기 때문입니다.
lvalue는 프로그램이 접근할 수 있는 주소가 있습니다. 변수 명, const 변수, 배열 요소, lvalue를 참조를 반환하는 함수 등의 표현식이 있습니다. rvalue 표현식은 프로그램에서 액세스할 수 있는 주소가 존재하지 않습니다.
정적 캐스트는 형 변환에 대한 타입을 런타임에 확인하지 않고 컴파일 시 정적으로 수행합니다. 동적 캐스트는 런타임에 상속 계층 관계를 가로지르거나 다운캐스팅할 때 사용됩니다. 기본 클래스 객체에 대한 포인터나 레퍼런스 타입을 자식 클래스의 타입으로 변환할 수 있고 nullptr이거나 예외를 보고 실패를 판별할 수 있습니다. 또한, 동적 캐스트는 다형성을 띄지 않는 객체간 변환은 불가능하고 컴파일 에러가 발생합니다.
정적 캐스트는 정적으로 형변환을 해도 아무런 문제가 발생하지 않는 것은 이미 어떤 것인지 알고 있을 경우이며, 동적 캐스트는 동적으로 형변환을 시도한다는 것은 그것의 타입을 반드시 알아봐야 한다는 것을 의미합니다. 해당 타입에 대해 명확히 알고 있다면 정적 변환을 써 비용을 절약하는 것이 좋습니다.
클래스는 기본 한정자로 private이며, 멤버 변수 외에도 멤버 함수를 가집니다. 또한 상속을 할 수 있는 반면에, 구조체는 변수들의 모임들로 멤버 함수를 가질 수 없고 기본 한정자는 public입니다.
파이썬과 C++ 모두 객체지향 언어 입니다. 파이썬은 인터프리터 언어로 소스 코드를 한줄 한줄씩 읽어 명령을 처리하기 때문에 컴파일러 언어보다 속도가 느립니다. C++은 컴파일러 언어이며 코드를 한번에 어셈블리 언어로 번역후 한번에 실행하는 언어입니다.
linking은 여러 개의 코드와 데이터(라이브러리)를 연결해 실행할 수 있는 하나의 파일로 만드는 작업을 말합니다.
static linking은 실행파일을 생성할 때 라이브러리를 같이 포함시켜서 exe 파일로 만드는 것을 말합니다. 만약 여러 프로그램에서 라이브러리 A를 사용할 때, 해당 프로그램의 오브젝트 파일 각각에 라이브러리 A의 정보가 포함됩니다. 중복이 발생하여 메모리 공간을 낭비하게 됩니다. 또한, 라이브러리에 변경이 생기면 변경 적용을 위해 다시 컴파일하여 링킹을 해야합니다. 모든 코드는 하나의 실행 모듈에 담기기 때문에 불일치가 발생하지 않고 동적 링킹 방식보다 빠르다는 장점이 있습니다.
dynamic linking은 오브젝트 파일을 만들 때, 프로그램에서 사용하는 모든 라이브러리들을 복사하지 않고 해당 주소만 가지고 있다가 런타임에 실행 파일과 라이브러리가 메모리에 적재될 때 해당 라이브러리의 주소로 가져오는 방식입니다. 이 방식을 사용하면 여러 프로그램에서 라이브러리 A를 사용한다 해도, A의 주소 하나만 가지고 있으면 됩니다. 따라서 오브젝트 파일의 크기가 작아 메모리 공간을 아낄 수 있게 됩니다. 매번 주소로 가야하므로 오버헤드가 발생하여 정적 링킹보다 느립니다. 또한, 불일치 문제가 발생할 수 있습니다. 왜냐하면 프로그램에서 A를 동적 링킹 방식으로 할 때, A가 제거될 경우 더 이상 참조할 함수가 없게 되기 때문입니다.
class/struct 는 기본 접근자가 private/public 말고는 차이가 없습니다