Hyun2and

[Effective C++] 1. C++ 본문

공부엔 끝이없다/Effective C++

[Effective C++] 1. C++

혀니앤 2023. 2. 19. 22:45
728x90

1: C++ 언어들의 연합체

  • C++는 다중패러다임 프로그래밍 언어 라고 부른다
  • 절차적 프로그래밍을 기본으로 하여 객체 지향, 함수식, 일반화 프로그래밍, 메타 프로그래밍을 포함한다
  • TMP : 템플릿 메타프로그래밍.
    • C++의 일반화 프로그래밍 부분
  • STL : 템플릿 라이브러리
    • 컨테이너, 반복자, 알고리즘, 함수 객체
  • C와의 차이점
    • C에서는 기본적으로 값 전달이 참조 전달보다 좋다.
    • C++에서는 생성자, 소멸자가 생기고, 템플릿을 쓰다보면 해당 객체의 타입을 알 수 없기 때문에 const 객체 전달이 가장 좋다
    • 그러나, STL은 값 전달이 좋다 (C 기반이기 때문에)

 


2: #define 보다 const, enum, inline

  • 컴파일러를 더 가까이하자
  • #define 을 쓰면 선행 처리 과정에서 이미 상수로 바뀐다
  • 상수 정의는 헤더 파일에 넣는 것이 상례
  • 포인터, 포인터가 가리키는 대상 const
  • 클래스 내부 상수를 정의하는 경우, static 멤버로 만들어야 한다
  • 매크로는 정의되면 컴파일이 끝날 때까지 유효하다

Enum hack 나열자 둔갑술

  • C++ 에서는 enumerator 타입에 int 가 놓일 수 있다

enum { NumTurns = 5; } 로 선언하고 int scores[NumTurns]; 해도 문제가 없다

  • 동작 방식이 const보다는 #define에 가깝다
    • 주소를 얻어내거나 참조자를 쓸 수 없다
  • 쓸데없이 메모리가 잡히지 않는다
  • 메타프로그래밍 핵심 기법

inline

  • 호출 매크로와 비슷한 #define 으로 함수를 많이 정의한다
    • 이 경우, 함수 결과에 따라 다른 결과값이 나올 수도 있다
  • lnline을 쓰면 동일 계열 함수군 family of functions 를 만들어낸다
  • inline으로 선언된 함수는 진짜 함수이기 때문에 유효범위와 접근 규칙을 그대로 따른다 (#define은 전혀 없다)

 


3: const를 자주 사용하기

  • const를 사용하면 이 객체를 외부에서 변경이 불가능해지며, 컴파일러가 이 제약을 단단히 지켜준다.
  • 클래스 바깥에서 전역 혹은 네임스페이스 유효범위의 상수를 선언하는 데 쓸 수 있다
  • 파일, 함수, 블록 유효범위에서 static으로 선언한 객체에도 const를 붙일 수 있다
  • 클래스 내부에서는 정적 멤버 및 비정적 데이터 멤버 모두를 상수로 선언할 수 있다
  • STL iterator는 포인터를 본따 만들어졌다. 기본 동작 원리가 T*와 유사
  • 반복자는 가리키는 대상 자체는 변경이 가능하다. (반복자 자체를 변경하려면 const_iterator 사용)

Const 멤버 함수

  • 해당 멤버 함수가 상수 객체에 호출될 함수라는 것을 알려준다
    • 클래스의 인터페이스의 이해가 좋아진다
    • 상수 객체를 사용할 수 있게 된다 (객체 전달을 const 참조로 하는 것이 가장 효율적이기 때문)
  • const 키워드가 있고, 없는 차이만 있는 함수들은 오버로딩이 가능하다
  • 상수성 const 의 개녑
    • 비트수준 상수성 bitwise const (물리적 physical) : 어떤 멤버 함수가 그 객체의 어떤 데이터 멤버도 건드리지 않아야 const로 인정함
    • 논리적(logical) 상수성 : 일부 몇 비트가 수정되더라도 사용자 수준에서 인식할 수 없다면 const로 인정함→ mutable : 비정적 데이터 멤버를 bitwise const 에서 풀어준다. 약간의 변경만 하고 컴파일러도 피할 수 있다
    • → 하지만 , 컴파일러의 검열을 통과하려면 bitwise const를 지켜야 한다.

코드 중복 현상을 피하기

  • operate[ ] 를 const, 비 const 로 오버라이딩했을 때, 동일한 operator 연산이므로 같은 내용을 두 번 작성해야 한다 = 코드 중복!
  • 비 const 타입 함수에서 const 타입을 호출해주자. *this 타입 캐스팅을 쓰면 된다

 


4: 객체를 사용하기 전에 반드시 그 객체를 초기화할 것

  • C++ 에서는 기본적으로 객체 초기화를 해주지만, 안해주는 경우도 있다
  • 규칙이 존재하긴 하지만 복잡하므로… 사용하기 전에 항상 초기화하자
  • 대입(alignment) 과 초기화 (initialization) 는 다르다
    • 클래스의 생성자에 값을 전달하는 방식은 대입이다. 생성자의 본문이 실행되기 전에 초기화되어야 한다
class A(){
};

A::A(string name, ... ){ 
	thename = name;  // alignment
}

A::A(string name, ... ) : thename(name) { // initialization
} 

  • 특정 값을 넣지 않더라도 초기화해주는 것이 좋다
A::A() : theName(), theAddress(), numTimes(0) {}

 

객체를 구성하는 데이터의 초기화 순서

  • 객체 초기화는 변덕스럽지만, 데이터 초기화 순서는 그렇지 않다
  1. 기본 클래스는 파생 클래스보다 먼저 초기화된다
  2. 클래스 데이터 멤버는 선언된 순서대로 초기화된다
  • static object : 자신이 생성된 시점부터 프로그램이 끝날 때까지 살아 있는 객체
    • 전역 객체
    • 네임스페이스 유효범위에서 정의된 객체
    • 클래스 안에서 static으로 선언된 객체
    • 함수 안에서 static으로 선언된 객체 = local static object
    • 파일 유효범위에서 static으로 정의된 객체

(함수 제외하고는 모두 non-local static object)

  • translation unit : 컴파일을 통해 하나의 object file을 만드는 바탕이 되는 소스코드

→ 별개의 translation unit에서 정의된 non-local static object들의 초기화 순서는 정해져 있지 않다

→ non-local static object 를 하나씩 맡는 함수를 준비하고 이 안에 객체를 넣는다. → local static 객체로 만든다 (=singleton )

 

728x90
Comments