[Effective C++] 07. operator= 에서는 자기대입에 대한 처리가 빠지지 않도록 하자
·
C++/[서적] Effective C++
자기 대입class Widget { };Widget w;w = w; // 자기 대입자기 대입이란 어떤 객체가 자기 자신에 대해 대입 연산자를 적용하는 것을 의미한다.이러한 자기 대입은 여러 곳에서 하나의 객체를 참조하는 상태, 중복참조(aliasing)라고 불리는 것 때문에 발생한다.위의 경우 문제가 되지 않지만, 리소스를 직접 관리하는 클래스의 경우 문제가 발생한다. 문제 상황class Bitmap { };class Widget {...private: Bitmap *pb;};Widget& Widget::operator=(const Widget& rhs){ delete pb; pb = new Bitmap(*rhs.pb); return *this;}// main.cppint ..
[Effective C++] 06. 대입 연산자는 *this의 참조자를 반환하게 하자
·
C++/[서적] Effective C++
개요int x, y, z;x = y = z = 15;// x = (y = (z = 15));C++의 대입 연산은 여러 개가 사슬처럼 엮일 수 있는 재미있는 성질을 가지고 있다. 이때 대입 연산은 다음과 같이 함수로 풀어낼 수 있다.x.operator=(y.operator=(z.operator(15))); 구현 분석이제 대입 연산자의 내부 구현을 살펴보자.class MyClass {public: MyClass& operator=(int value) { // ... return *this; }};보통, 자기자신에 대한 참조 객체를 반환하도록 구현한다. 이는 대입 연산을 연쇄적으로 가능하게 함과 동시에 메모리적으로 효율성을 높일 수 있다.class MyClass {publ..
[Effective C++] 05. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자
·
C++/[서적] Effective C++
작동 방식(1) 생성자class Base {public: Base() { Initialize(); // Base::Initialize()가 호출됨 } virtual void Initialize() { UE_LOG(LogTemp, Log, TEXT("Base Init")); }};class Derived : public Base {public: Derived() : Base() { // Base 생성자 실행 시점에는 아직 Derived 객체가 완성되지 않음 } virtual void Initialize() override { UE_LOG(LogTemp, Log, TEXT("Derived Init")); //..
[Effective C++] 04. 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자
·
C++/[서적] Effective C++
부분 소멸(partially destroyed)class TimeKeeper {public: TimeKeeper(); ~TimeKeeper();};class AtomicClock : public TimeKeeper { };class WaterClock : public TimeKeeper { };class WristWatch : public TimeKeeper { };TimeKeeper를 상속받는 클래스에서 팩토리 함수(Factory Function)을 다음과 같이 구현했다고 가정하자.팩토리 함수 : 새로 생성된 파생 클래스 객체에 대한 기본 클래스 포인터를 반환하는 함수TimeKeeper* getTimeKeeper();getTimeKeeper 함수에서 반환되는 객체는 힙에 있게 되므로, 메모리..
[Effective C++] 03. C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자
·
C++/[서적] Effective C++
개요빈 클래스의 경우, 상황에 따라 컴파일러는 기본 생성자 / 소멸자 / 복사 생성자 / 복사 대입 연산자를 public, inline으로 선언해 놓는다. 어떤 상황에서 자동으로 생성되는지 살펴보자.// 자동 생성 예시class Empty{public: // 생성자 Empty() { } // 소멸자 : 가상 여부에 따라 다름 ~Empty() { } // 복사 생성자 Empty(const Empty& rhs) { } // 복사 대입 연산자 Empty& operator=(const Empty& rhs) { }}; 1. 기본 생성자 / 소멸자Empty e1; // 기본 생성자, 소멸자 선언클래스의 인스턴스를 생성하는 경우 해당 클래스에 생성자,..
[Effective C++] 02. 객체를 사용하기 전에 반드시 그 객체를 초기화하자
·
C++/[서적] Effective C++
상황에 따른 초기화 여부1. 초기화가 보장되는 경우 (1) 정적 저장 기간// 전역 변수Point p; // x, y = 0으로 초기화됨// static 변수void func() { static Point p; // x, y = 0으로 초기화됨} (2) 명시적 값 초기화Point p{}; // x, y = 0으로 초기화됨Point p = {}; // x, y = 0으로 초기화됨Point p = Point(); // x, y = 0으로 초기화됨 2. 초기화가 보장되지 않는 경우(1) 지역 변수void func() { Point p; // x, y는 쓰레기 값 (undefined behavior)}int main() { Point p; // x, y는 쓰레기 값..
[Effective C++] 01. #define을 쓰려거든 const, enum, inline을 떠올리자
·
C++/[서적] Effective C++
#define(1) 매크로 상수#define ASPECT_RATIO 1.653컴파일 하기 전에 선행 처리자가 ASPECT_RATIO를 숫자 상수로 바꾸어 버린다고 한다. 따라서, 컴파일러가 쓰는 기호 테이블에 들어가지 않고, 에러 메시지 또한 ASPECT_RATIO가 아닌 1.653으로 출력한다고 한다. 메모리 측면에서도 비효율적인데 ASPECT_RATIO가 등장할 때마다 1.653의 사본이 들어가게 되기 때문이다.(2) 매크로 함수// a와 b 중에 큰 것을 f에 넘겨 호출합니다.#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))매크로를 작성할 때에는 연산자 우선순위가 중요하게 작용하기 때문에 인자마다 괄호를 씌워 주어야 한다. 하지만, 괄호만으로는 해결할 수..
[이것이 C++이다] 11. 람다
·
C++/[서적] 이것이 C++이다
1. 람다식익명 함수를 생성하는 간결한 방법이다.[capture](parameters) -> return_type { body }auto TestFunc = [nData](void)->void // 모두 기술auto TestFunc = [nData](void) // 반환 형식 생략auto TestFunc = [nData]() // 반환 형식, 매개변수 생략auto TestFunc = [nData] // 모두 void이므로 괄호까지 생략 (1) 기본 사용법int main(){ // 람다식 선언 및 정의 auto func = [](int nParam) -> int { cout (2) 함수 매개변수로 사용std::function..