제목과 같은 explicit, volatile 키워드에 대해 포스트를 작성하게 된 계기는 최근 Effective Modern C++를 읽는 동안 예제 코드에서 간간히 등장해서 확실히 알고 있는게 좋을 것 같아서이다.
짧고 간략하게 써보겠다.
explicit 키워드는 형변환과 관련된 키워드이다.
그 중에서 형변환 시 묵시적(암묵적) 형변환을 하지 못하도록 막는 키워드인데 단어의 뜻에서 알 수 있듯이 명백히 형변환하라로 해석했다.
보통 클래스의 생성자에서 사용되는데, 해당 생성자가 암시적 형변환을 허용하지 않도록 지정하는데 사용된다. 즉, explicit 키워드를 사용하면 해당 생성자가 명시적으로 호출되어야 한다
class Person
{
public:
Person(int InAge) { age = InAge; }
void Print() { cout << age << '\n'; }
private:
int age;
};
int main()
{
Person jaehoon = 26; // 암묵적 형변환
jaehoon.Print();
}
// 결과 : 26
Person 객체 jaehoon을 int 타입인 26으로 초기화했더니 Person(int InAge) 생성자가 호출되어 멤버 변수 age를 초기화시켰다. 이것이 바로 int에서 Person(int)로의 암묵적 형변환이다.
여기서 Person(int) 생성자 앞에 explicit 키워드를 지정해보면
class Person
{
public:
explicit Person(int InAge) { age = InAge; }
void Print() { cout << age << '\n'; }
private:
int age;
};
int main()
{
// Person jaehoon = 26; 컴파일 에러가 난다
Person jaehoon(26);
jaehoon.Print();
}
앞에서처럼 '='를 사용해 초기화하면 컴파일 에러가 나며 명시적 호출을 통해 해결했다
예제를 통해 살펴본 결과 explicit을 사용하는 이유를 대강 알 것 같다
1. explicit을 사용하면 생성자를 명시적으로 호출해야 한다는 의도를 코드에서 명확하게 표현이 가능하다
2. 암묵적 형 변환은 의도치 않은 객체 생성으로 이어질 수 있어 오류를 방지할 수 있다
그러면 주의 사항은 무엇일까?
1. explicit 키워드를 사용한 생성자를 모두 명시적으로 호출해야 하기에 코드가 다소 불편해질 수도 있다
2. explicit 키워드를 기본 생성자에는 사용하면 안된다(Person()같이). 기본 생성자는 암묵적 형변환을 사용할 수 있도록 설계되어 있음
volatile은 C++ 키워드 중 가장 사용 빈도가 낮은 키워드이지만 알아두면 나쁠 것은 없으니까 살펴보겠다.
const 키워드 같이 변수 앞에 붙여 변수가 외부적인 요인에 의해 그 값이 언제든지 바뀔 수 있음을 의미한다.
컴파일러는 volatile로 선언된 변수에 대해서는 최적화를 수행하지 않으며 volatile 변수를 참조할 경우 레지스터에 로드된 값을 사용하지 않고 매번 메모리를 참조한다.
다음은 앞에서 말한 컴파일러의 최적화란 내가 이해한 바이다
만약 같은 주소 공간에 값을 쓰는 연산이 5개 있을 때 컴파일러는 같은 주소 공간의 값만 필요하니까 마지막 연산이 중요하므로 속도를 향상시키기 위해 앞에 불필요한 4개의 연산을 수행하지 않고 마지막 연산만 수행한다
이런 최적화가 필요하지 않을 때에 쓸 수 있는 키워드가 바로 volatile이다
세 가지 모두 현재 프로그램의 수행 흐름과 상관없이 외부 요인이 변수 값을 변경할 수 있다는 점이다.
인터럽트 서비스 루틴이나 멀티 쓰레드 프로그램의 경우 일반적으로 스택에 할당하는 지역 변수는 공유하지 않으므로, 서로 공유되는 전역 변수의 경우에만 필요에 따라 volatile을 사용한다