기본 콘텐츠로 건너뛰기

C++ explicit

보이지 않는 위험, 암묵적 형변환. C로부터 계승해온 아주 멋지구리한 녀석이지. 뭐, 나름대로 편하긴 해도 벌레 잡을 때 그지 같단 말이지.

아래 코드를 보자.
#include <iostream>
using namespace std;

// -DDO_EXP 를 컴파일 옵션에 넣으면 실험할 수 있다.
#if !defined(DO_EXP)
#   define EXPLICIT
#else
#   define EXPLICIT explicit
#endif

// 몇몇 정보를 찍는 함수
template<typename _T>
static
void
_showme(const char* fn, void* ptr, const _T& val)
{
    cout << fn << '(' << ptr << "): " << val << endl;
}

// 몇몇 정보를 찍는 매크로
#define showme(h) _showme(__PRETTY_FUNCTION__, \
    this, \
    (h) )

// 내 클래스
class CMyClass
{
public:
    // 생성자1 - a
    EXPLICIT CMyClass()
    {
        showme(this);
    }

    // 생성자2 - b
    EXPLICIT CMyClass(int h)
    {
        showme(h);
    }

    // 생성자3 - c
    EXPLICIT CMyClass(const CMyClass& h)
    {
        showme(&h);
    }

#if 0
    // 대입연산자1 - d
    CMyClass& operator = (int h)
    {
        showme(h);
        return *this;
    }
#endif

    // 대입연산자2 - d
    CMyClass& operator = (const CMyClass& h)
    {
        showme(&h);
        return *this;
    }
};

int
main(int,char**)
{
    CMyClass h;
    h = 10;
    return 0;
}
일단 컴파일을 아래와 같이 해보자.
$g++ -o explicit explicit.cpp
별 이상 없이 컴파일하는 것을 볼 수 있다. 실행하면 아래와 같이 나온다.
$./explicit
CMyClass::CMyClass()(0xbffaf6c0): 0xbffaf6c0
CMyClass::CMyClass(int)(0xbffaf6b0): 10
CMyClass& CMyClass::operator=(const CMyClass&)(0xbffaf6c0): 0xbffaf6b0
'operator ='이 아니라 생성자가 찍힌 이유는 빨갛게 칠해진 부분을 컴파일러가 아래와 같이 암묵적으로 해석했기 때문이다.
{
    CMyClass _tmp(10);
    h.operator = (_tmp);
    // _tmp.delete();
}
어째서? 라고 한다면 C++ 암묵적인 형변환 덕(?)뿐이라고 하겠다. 신발놈. 만약 '대입연산자1 - d'를 살려놨으면 그 녀석을 바로 실행했겠지만, 없다고 해서 컴파일러 입장에서 방법이 없는게 아니라서 저렇게 나름대로 길을 열어준거다. 근데 저거 별루 좋아보이진 않는데 말이지. 벌레 잡을 때 다른 코드만 열라 뒤질 가능성이 농후하단 말이지.

그래서 여러가지 형태를 가지는 생성자 맨 앞에 explicit을 써주면 저런 돌려서라도 되게 만들어주는 암묵적 형변환을 막을 수 있다. 위 소스에서 아래와 같이 -DDO_EXP 컴파일 옵션을 주고 컴파일하면 오류가 발생한다. (저 옵션은 위 소스에만 쓰일꺼니깐 괜스레 관심 갖지 말고)
g++ -O1 -DDO_EXP    explicit.cpp   -o explicit
explicit.cpp: In function `int main(int, char**)':
explicit.cpp:68: error: no match for 'operator=' in 'h = 10'
explicit.cpp:58: note: candidates are: CMyClass& CMyClass::operator=(const CMyClass&)
형변환해서 새로운 객체를 만들 생각은 하지 않고, 'operator = (int)'를 찾는 모양인데, 없어서 에러를 퉤!하고 내뱉고 끝난다.

아무튼 암묵적 형변환에 너무 맡기면 벌레잡기 힘들어지니 잘 활용하도록 하자. explicit는 C++ 표준이며, 생성자 앞에만 쓸 수 있다.

댓글

이 블로그의 인기 게시물

버즈 라이브 배터리 교체

나는 버즈 라이브(SM-R180)가 좋은데, 평가가 별루였는지, 해당 스타일로 버즈를 더 이상 만들지 않고 있다. 아무튼, 오래 쓴 버즈 라이브 배터리가 슬슬 맛이 가기 시작해서, 블로그 를 참조하면서 분해 및 교체를 하였다. (진짜 쉬움) 요로코롬 위아래를 살짝 눌러주면 뚜껑이 벌어진다. 안쪽 플라스틱은 오른쪽은 분홍색, 왼쪽은 회색이다. 리본 케이블 살짝 들어내고, 기판을 떼어내면, 작은 나사가 있다. 나사를 풀고, 플라스틱을 걷어내면, 검은 양면 테이프로 고정된 CR1254 배터리가 보인다. 잘 쑤셔서(?) 꺼낸다. 새로운 CR1254 배터리를 넣는다. 음극이 아래로 가도록 하고, 분해의 역순으로 조립하면 된다. 조립할 때, 아까 풀었던 나사는 잊지 말고 꼭 조여준다. (까먹고 조립해서 다시 뜯고 조립함) 충전도 잘 되고, 소리도 잘 나는거 보면, 조립도 잘 된 것 같다. 이렇게 버즈 라이브의 수명을 강제로 늘렸다. 나중에 본체 배터리도 갈아야겠다.

Windows 에서 절전을 깨우는 장치 찾기

참조:  https://www.reddit.com/r/computer/comments/wquswv/windows_11_pc_wakes_up_every_time_i_move_usb/ powercfg /devicequery wake_armed powercfg /deviceenablewake "[DEVICE]" # $PROFILE function Get-WakeArmedDevices { $devices = powercfg -devicequery wake_armed if ($devices) { $devices | ForEach-Object { $_.Trim() } } else { Write-Host "No devices are currently armed for wake events." } } function Set-EnableWakeOnDevice { param( [string]$deviceName ) sudo powercfg -deviceenablewake $deviceName } function Set-DisableWakeOnDevice { param( [string]$deviceName ) sudo powercfg -devicedisablewake $deviceName }