DefineVsFinalStatic

  • 상수 정의가 필요할 때

  • 재사용성이 좋습니다. (이식성과 유지보수성이 좋다는 소리와 같습니다)

    • 유지 보수성 : 여러 파일 간에 공통으로 사용되는 값을 중앙집중적으로 관리 가능합니다.

  • 컴파일 전에 소스 코드를 유연하게 조작 할 필요가 있었습니다.

    • 예시 : 조건부 컴파일이 가능함

    #include <iostream>
    
    #define DEBUG  // 이 줄을 주석 처리하면 Release 모드로 동작합니다.
    
    int main() {
    #ifdef DEBUG
        std::cout << "Debug mode" << std::endl;
    #else
        std::cout << "Release mode" << std::endl;
    #endif
        return 0;
    }

장점

  • 메모리 효율성:

    • 전처리 단계에서 리터럴로 치환되므로 런타임에 추가 메모리 할당 없이 값이 직접 코드에 삽입됩니다.

    💡

    간단한 설명

    • 자바의 경우 멤버 변수의 값을 재사용 할 때, 새로운 변수에 담아서 재사용하는 경우 그만큼의 메모리가 추가됨

    • define 은 전처리 단계에서 호출이 아닌 진짜 코드 자체를 해당 텍스트로 변경하기 때문에 재사용시 추가 메모리를 차지하지 않음.

      • 추가로 define 으로 정의된 함수의 경우 스택 프레임 생성, 반환 주소 저장등의 오버헤드가 줄어듬.

  • 컴파일 타임 최적화:

    • 상수 값이 코드에 직접 삽입되므로 함수 호출 오버헤드 없이 빠르게 사용할 수 있습니다.

  • 유지보수 용이성:

    • 상수 값이나 반복되는 코드 조각을 한 곳에서 관리할 수 있어, 변경 시 여러 부분을 일일이 수정할 필요 없이 한 번에 수정 가능합니다.

  • 조건부 컴파일:

    • #ifdef, #ifndef 등을 사용하여 플랫폼별 또는 상황별로 코드를 선택적으로 컴파일할 수 있습니다.

단점

  • 타입 검사 없음:

    • 단순 텍스트 치환이기 때문에, 타입 안전성이 보장되지 않습니다. (예를 들어, 잘못된 타입의 값이 삽입될 수 있음)

  • 디버깅 어려움:

    • 전처리 단계에서 치환되어 소스 코드에는 원래 #define 문이 보이지 않으므로, 디버깅 시에 실제 치환된 코드 추적이 어려울 수 있습니다.

  • 예측 불가능한 부작용:

    • 복잡한 매크로는 치환 과정에서 예상치 못한 결과를 초래할 수 있고, 코드 유지보수 시 혼란을 줄 수 있습니다.

자바의 static final과의 차이점

  • 실행 방식:

    • C++의 #define:

      • 전처리 단계에서 텍스트 치환이 이루어지므로, 런타임에 별도의 메모리 할당 없이 코드에 직접 삽입됩니다.

    • 자바의 static final:

      • 클래스의 멤버 변수로서 런타임에 상수 풀 혹은 클래스 정적 데이터 영역에 할당되어, JVM이 관리합니다. 단, 컴파일 타임 상수인 경우 인라인 최적화가 일어날 수 있습니다.

  • 타입 검사:

    • *#define*은 단순 텍스트 치환이므로 타입 안전성이 없고, 컴파일러가 타입을 확인하지 않습니다.

    • *static final*은 명시적인 타입 선언으로, 컴파일러가 타입 검사를 수행합니다.

  • 디버깅 및 유지보수:

    • *#define*은 전처리 후 소스 코드에 직접 보이지 않기 때문에, 디버깅 시에 원래의 매크로가 어떻게 치환되었는지 확인하기 어려울 수 있습니다.

    • *static final*은 클래스의 일부로 존재하기 때문에, 디버깅 시에도 상수의 값과 타입을 쉽게 확인할 수 있습니다.

Last updated