C++ 고급 문법 (20) — CRTP(Curiously Recurring Template Pattern)
지난 글에서 본 가상 함수는 호출마다 vtable을 거치는 간접 호출이라 인라인되지 않는다. 하지만 어떤 파생 타입을 쓸지 컴파일 시점에 이미 정해져 있다면 실행 시점 디스패치는 필요 없다. CRTP(Curiously Recurring Template…
지난 글에서 본 가상 함수는 호출마다 vtable을 거치는 간접 호출이라 인라인되지 않는다. 하지만 어떤 파생 타입을 쓸지 컴파일 시점에 이미 정해져 있다면 실행 시점 디스패치는 필요 없다. CRTP(Curiously Recurring Template…
shape.area()라는 호출이 실제로 어느 함수로 이어질지는 두 시점 중 하나에 정해진다. 컴파일할 때 정적 타입만 보고 정하면 정적 바인딩(static binding, early binding), 실행할 때 객체의 실제 타입을 보고 정하면 동적…
파생 클래스 객체를 기반 클래스의 포인터나 참조로 다루는 것을 업캐스팅(upcasting)이라 한다. public 상속이면 캐스트 없이 암시적으로 일어나고, 다형성은 이 변환을 전제로 동작한다. 하지만 같은 업캐스팅이라도 포인터·참조가 아니라 값으로 일어나면 파생…
public 상속은 "파생 클래스는 기반 클래스의 일종이다(is-a)"를 뜻해서, 파생 객체를 기반 참조로 넘길 수 있고 기반의 public 인터페이스가 그대로 노출된다. private 상속은 의미가 다르다. 기반 클래스의 구현을 가져다 쓰지만 외부에는…
생성자를 public이 아닌 곳에 두면 "누가 이 객체를 만들 수 있는가"를 클래스가 직접 정할 수 있다. protected 생성자는 파생 클래스만 기반 부분을 만들게 해서, 순수 가상 함수 없이도 기반 클래스를…
new Widget(7)은 메모리를 할당하는 동시에 생성자를 호출한다. 이 한 줄 안에는 메모리를 얻는 operator new 함수 호출과 그 위에서 객체를 만드는 생성자 호출이 순서대로 들어 있다. 두 단계를 분리해서 보면…
send(1024)가 컴파일되고 1024바이트짜리 버퍼가 전송된다. send는 Buffer를 받는 함수인데, Buffer에 크기를 받는 생성자가 있어서 컴파일러가 임시 객체를 만들어 넘긴 것이다. C++은 생성자와 변환 연산자를 통해 사용자 타입 사이에도 암시적 변환(implicit…
new로 만든 객체는 누군가 정확히 한 번 delete해야 한다. 함수 중간에 return이나 예외가 끼면 해제가 빠지고, 두 곳에서 해제하면 이중 해제가 된다. 스마트 포인터(smart pointer)는 이 "누가 언제 해제하는가"를 타입으로…
로그를 백만 줄 쓰는 코드에서 std::endl을 '\n'으로 바꾸기만 했는데 실행 시간이 수십 배 줄어든다. std::endl은 줄바꿈과 함께 버퍼를 비우는(flush) 조작자이고, flush는 매번 시스템 호출로 이어진다. std::cout에는 이 밖에도 C stdio와의…
벡터 덧셈을 add(a, b) 대신 a + b로 쓰고, 사용자 타입을 std::set에 넣으려면 연산자 오버로딩(operator overloading)이 필요하다. 연산자는 이름이 operator+인 함수일 뿐이지만, 멤버로 둘지 비멤버로 둘지, 무엇을 반환할지에 따라 대칭성·성능·의미가…