VioletaBabel

1일 : define, const, enum, inline, 객체 초기화, 빈 클래스 본문

기본개념/Effective C++
1일 : define, const, enum, inline, 객체 초기화, 빈 클래스
Beabletoet 2018. 2. 19. 16:09

2. #define을 쓰려거든 const, enum, inline을 떠올리자


 - const

1
#define A_RATIO 1.653
cs

을 썼는데 에러가 난다면 에러메시지에는 A_RATIO가 아닌 1.653으로 나온다.

매크로를 쓸 일이 생긴다면 매크로 대신 상수를 이용해

1
const double Aratio = 1.653;
cs

같은 식으로 쓰는게 좋다.

매크로를 쓰면 A_RATIO가 등장할 때마다 1.653의 사본이 생겨 들어가지만, const로 선언 시에는 사본은 딱 하나만 생기기에 컴파일 후 최종 코드의 크기가 줄어든다.


상수 포인터를 정의하는 경우에는 

1
const char* const Name = "Yuna Kim";
cs

처럼 포인터와 포인터가 가리키는 대상까지 const로 선언해줘야 한다.


문자열 상수를 쓸 때에는 char*보다 string 객체가 좋은데

1
const std::string Name("Yuna Kim");
cs

같은 식으로 정의한다.



클래스 멤버로 상수를 정의하는 경우, 상수의 유효범위를 클래스 하나로만 한정하여 사용할 때 static const를 사용하면 상수의 사본이 하나로 한정되어 좋다.

단, 일반적인 static 멤버 변수와 다르게 클래스 안에서 초기화를 한다.

1
2
3
4
5
6
7
class A
{
public:
    A();
private:
    static const int a = 10;
};
cs



 - enum hack(나열자 둔갑술)

동작 방식은 const보다 #define에 가깝다.

1
2
3
4
5
6
7
class A
{
private:
    enum { a = 10 };
public:
    int score[a];
};
cs

같은 식으로 사용하는데, #define과 동작 방식이 비슷하다. 예를 들어 const의 주소를 잡아낼 수는 있지만, enum의 주소나 #define의 주소를 취하는 것은 불가능하다. 그러나 enum hack 방식은 #define과 달리 어떤 형태의 쓸데없는 메모리 할당도 저지르지 않는다. (하지만, 오로지 정수형 상수를 만드는데에만 쓰임을 명심하자.)



 - inline

인라인 함수란 프로그램의 실행 속도를 높이기 위해 함수를 호출하는 대신, 그에 대응하는 함수 코드로 치환되는 함수이다. 함수 간의 이동을 위해 점프할 필요 없이 즉석으로 치환하므로 속도 점에선 유리하지만, 컴파일 후의 코드를 보면 길어지는 단점이 있어 성능이 오히려 저하될 수 있으므로 짧은 코드의 함수에만 인라인을 쓰는게 좋다.

1
#define MAX(a,b) f((a)>(b)?(a):(b))
cs
같은 매크로 대신

1
2
3
4
5
template<typename T>
inline void Max(const T& a, const T& b)
{
    f(a > b ? a : b);
}
cs

같은 식으로 사용한다.



 - 정리 -

단순한 상수를 쓸 때는 #define보다 const나 enum hack을, 함수처럼 쓰이는 매크로는 #define보다 inline을 사용하라.



======================


3. const를 들이대라.

1
2
3
4
5
6
7
8
    char a[] = "good";
    char *p1 = a; // 비상수 포인터, 비상수 데이터
    const char *p2 = a; // 비상수 포인터, 상수 데이터
    char const *p2_2 = a; // 비상수 포인터, 상수 데이터
    char* const p3 = a; // 상수 포인터, 비상수 데이터
    const char* const p4 = a; // 상수 포인터, 상수 데이터
    //const 키워드가 *의 왼쪽에 있으면 포인터가 가리키는 대상이 상수
    //const 키워드가 *의 오른쪽에 있으면 포인터 자체가 상수
cs


1
2
3
4
5
6
7
const std::vector<int>::iterator i = vec.begin();
*= 10// i가 가리키는 대상을 변경
//++i; 같은 경우는 i가 상수여서 불가능
 
std::Vector<int>::const_iterator cI = vec.begin();
//*cI = 10; *cI가 상수이기에 불가능
++cI;
cs
여기서 iterator(반복자)는 컨테이너 내의 원소를 순회할 수 있게 하는 객체이다.


const를 함수의 리턴 타입에 붙여주면 사용자의 실수를 줄여주기도 한다.



 - 상수 멤버 함수

멤버 함수에 붙는 const의 역할은 해당 멤버 함수가 상수 객체에 대해 호출될 함수라고 알려주는 역할.

클래스의 인터페이스를 이해하기 좋게 하기 위해서(그 클래스로 만든 객체를 변경할 함수는 무엇인가 등을 사용자가 미리 알아야 함.)와 상수 객체를 사용하기 위해서(코드 효율 상 아주 중요한 부분으로, 상수 객체에 대한 참조자(reference to const)라고 함) 이 항목을 잘 알아야 한다.


const가 있느냐 없느냐의 차이로 오버로딩이 가능해진다.


1
2
3
4
5
const char& operator[] (std::size_t p) const
return text[p]; }//상수 객체에 대한 operator[]
 
char& operator[] (std::size_t p)
return text[p]; }//비상수 객체에 대한 operator[]
cs
여기서 1번 줄의 가장 끝 const처럼 함수가 클래스의 멤버인 경우, const 키워드를 함수 선언 뒤에 삽입 가능. 함수 선언과 함수 body 사이에 const를 두면 해당 함수가 속한 객체의 멤버를 변경할 수 없다.



 - 정리 - 

const를 붙여 선언하면 컴파일러가 사용상의 에러를 잡는데 도움을 준다. 그리고 상수 멤버와 비상수 멤버 함수가 기능적으로 똑같게 구현될 경우에는, 코드 중복을 피하기 위해 비상수 버전이 상수 버전을 호출하도록 짜라.



=============================


4. 객체를 사용하기 전에 반드시 그 객체를 초기화하라.


C++에서도 C부분은 초기화가 필수지만, vector처럼 STL 부분은 반드시 초기화된다는 보장이 있다. 하지만 가장 좋은 방법은 모든 객체를 사용하기 전에 항상 초기화하는 것. 특히 기본 타입으로 만들어진 것들은 반드시 손수 초기화할 것.


생성자에서는 멤버 초기화 리스트를 애용하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A
{
public:
    A(const string& _name, const string& _address);
private:
    string name;
    string address;
};
A::A(const string& _name, const string& _address)
{
    address = _address;
    name = _name;
    //안좋은 방식이다. 기본 생성자 호출 후, 대입 연산자를 여럿 호출하기 때문.
}
cs

1
2
3
4
5
6
7
8
9
10
class A
{
public:
    A(const string& _name, const string& _address);
private:
    string name;
    string address;
};
A::A(const string& _name, const string& _address):name(_name), address(_address)
{ }//생성자 본문에 아무 것도 없이, 이렇게 복사 생성자를 한 번 호출하는게 더 효율적.
cs


객체를 구성하는 데이터는 기본 클래스가 파생 클래스보다 먼저 초기화되고, 클래스의 데이터 멤버의 경우 선언된 순서대로 초기화된다.


초기화는 기본 생성자를 애용하자.


 - 정리 - 

기본 타입의 객체는 직접 손으로 초기화할 것. 생성자에서는 대입문 방식이 아닌, 멤버 초기화 리스트를 즐겨 사용할 것. 초기화 리스트에 데이터 멤버를 나열할 시는 클래스에 각 데이터 멤버가 선언된 순서와 똑같이 나열할 것.



=======================


5. C++은 은근슬쩍 함수를 만들어 호출한다.


비어있는 클래스더라도 생성자복사 생성자, 복사 대입 생성자, 소멸자가 컴파일러에 의해 저절로 선언된다.



========================


'기본개념 > Effective C++' 카테고리의 다른 글

2일 : 복사 금지 클래스, 가상 소멸자  (0) 2018.02.20
Comments