문제의 역사:
C++는 숫자, 문자, 배열과 같은 소규모 내장형 타입을 제공한 C를 기반으로 구축되었습니다. 언어 발전에 따라 구조체, 클래스, 열거형과 같은 개념이 추가되었고, 이들은 사용자 정의 타입이 되었습니다.
문제:
프로그램의 데이터 타입은 변수가 차지하는 메모리 크기, 초기화, 복사, 파괴, 비교 방식을 정의합니다. 내장형 타입은 표준에 따라 특정 행동을 가지며, 사용자 정의 타입은 모든 측면을 명시적으로 정의해야 합니다. 사용자 정의 타입 관리의 오류는 프로그램 충돌, 메모리 누수, 불공정한 객체 비교 등을 초래할 수 있습니다.
해결책:
내장형 타입은 int, float, double, char, bool 등 "프리미티브"로 간주됩니다. 사용자 정의 타입은 여러분이 만든 모든 구조체, 클래스, 공용체입니다. 복잡한 문제를 위해서는 복사, 비교, 자원 관리의 올바른 의미론을 갖춘 사용자 정의 타입을 도입해야 합니다.
코드 예시:
// 내장형 타입 int x = 5; // 사용자 정의 타입 struct Point { double x, y; }; Point a = {1.0, 2.0}; // 자원을 가진 클래스 class MyFile { public: MyFile(const std::string& fn) : f(fopen(fn.c_str(), "r")) {} ~MyFile() { if (f) fclose(f); } private: FILE* f; };
사용자 정의 타입이 내장형처럼 완전히 작동할 수 있습니까(예: ==로 비교 가능)?
아니요, == 비교는 C++20부터 기본으로 지원되는 defaulted operator==가 있어야 작동하고, 그 이전에는 명시적 정의가 필요합니다.
클래스가 오직 원시 포인터(int, FILE, 등)만 가진다면 복사 생성자와 소멸자를 구현하지 않아도 됩니까?**
아니요, 이 경우 기본적으로 복사는 "얕은" 복사가 되어 메모리/자원 누수 또는 이중 해제를 초래할 수 있습니다. "다섯 가지 규칙(Rule of Five)"을 구현해야 합니다.
구조체의 기본값이 0으로 초기화됩니까(Point p;)?
아니요, 지역 구조체는 초기화되지 않을 수 있으며, 메모리 내용이 임의적입니다. 명시적 초기화를 사용해야 합니다.
부정적인 케이스:
파일에 대한 래퍼 클래스가 소멸자를 구현하지 않았습니다. 프로그램은 디스크립터를 닫지 않고 수천 개의 파일을 처리하는 동안 작동했습니다.
장점: 코드량 감소, 단순한 외관 단점: 자원 누수, 불안정한 행동
긍정적인 케이스:
클래스는 RAII를 구현합니다. 파일은 생성자에서 열리고, 소멸자에서 닫히며 복사는 금지됩니다.
장점: 신뢰성, 안전성, 깔끔한 인터페이스 단점: "다섯 가지 규칙"을 기억하고 더 많은 코드를 작성해야 하는 필요성