본문으로 바로가기

C++11) explicit, delete, default, override

category C++/Modern 2019. 2. 6. 04:05

<개요>


c++11에서 추가된 explicit, delete, default, override 에 대해 작성합니다.

explicit 는 암시적인 형변환을 막아주는 키워드 입니다.

delete는 특정한 함수에 대해 정의를 금지하는 키워드 입니다.

default는 사용자 정의 타입이 기본적으로 만들어주는 생성자 함수를 명시적으로 만들것을 요구하는 키워드 입니다.

override는 사용자 정의 타입의 자식 클래스에게 함수를 오버라이딩 하라고 요구하는 키워드 입니다.

<explicit>


explicit는 암시적인 형변환을 막아주는 키워드 입니다.

예를 들어 Widget 이라는 사용자 정의 타입을 만들고 정수를 하나 받아 생성하게 만들었습니다.

그리고 print 라는 함수를 하나 정의해 Widget 의 값들을 출력하는 함수를 만들었습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
class Widget{
public:
    Widget(int _x) : a(_x) { }
    int getvalue() { return a; }
private:
    int a;
};
void print(Widget&& rhs) {
    cout << rhs.getvalue() << endl;
}
int main() {
    print(10);
}
cs

위 함수는 잘 돌아갈까요? 예 잘 돌아 갑니다.
10이라는 값을 통해 Widget의 임시객체가 만들어지고, 그 임시 객체의 getvalue 함수를 통해 10 이라는 값을 출력합니다.
자동 형 변환을 의도하지 않은 경우엔, 버그가 나타날 수도 있기때문에 이를 막기 위한 explicit 키워드가 등장했습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Widget{
public:
    explicit Widget(int _x) : a(_x) { }
    int getvalue() { return a; }
private:
    int a;
};
void print(Widget&& rhs) {
    cout << rhs.getvalue() << endl;
}
int main() {
    print(10);    //error, 암시적으로 임시객체로 출력할 수 없습니다.
    print(Widget(10));    //ok, 명시적으로 Widget의 임시객체를 만들어줬습니다.
}
cs

explicit 키워드를 사용한 뒤 부턴, 프로그래머가 직접적으로 형 변환을 해줘야하며 복사생성자와 이동생성자에도 적용 가능합니다.

<default>

1
2
3
4
5
6
7
8
9
class Widget{
public:
    Widget(const Widget& rhs) { }
private:
    int a;
};
int main() {
    Widget a; // error
}
cs

클래스는 기본적으로 기본 생성자, 소멸자, 복사 생성자, 대입 연산자, 이동 생성자, 이동 대입 연산자를 생성해줍니다.
하지만 기본 생성자는 복사 생성자 또는 이동 생성자가 정의되면 자동으로 만들어주지 않습니다.
그래서 기본 생성자를 새로 정의해주어야 하는데, default 함수로 컴파일러에게 명시적으로 만들것을 조금 더 간략하게 바꿀 수 있습니다.

1
2
3
4
5
6
7
8
9
10
class Widget{
public:
    Widget() = default;    // 생성자를 기본적으로 만들어 줍니다.
    Widget(const Widget& rhs) = default// 복사생성자도 기본적으로 만들어줘요
private:
    int a;
};
int main() {
    Widget a;
}
cs


하지만 default 키워드로 기본적으로 만든다면 얕은 복사만 가능합니다.
깊은 복사는 직접 정의해주셔야 합니다.

<delete>

C++11 이전엔 private 안에 복사 생성자와 대입 연산자를 정의하여 외부에서 접근하지 못하게 하는 방법 이였다면, 
이젠 delete 키워드를 통하여 특정 함수에 대한 정의를 금지할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Widget{
public:
    Widget() : a(nullptr) { }
    Widget(int _x) : a(new int) { *= _x; }
    Widget(const Widget& rhs) = delete;
    Widget(Widget&& rhs) = delete;
    Widget& operator=(Widget&& rhs) {
        a = rhs.a;
        rhs.a = nullptr;
        return *this;
    }
    ~Widget() { delete a; }
private:
    int* a;
};
int main() {
    Widget A(1);    // ok, 기본 생성자로 생성합니다.
    Widget B(Widget(1));    //ok, 복사생략으로 기본 생성자로 생성합니다.
    Widget C;
    C = Widget(10);    //ok, 이동 대입 연산자로 초기화 됩니다.
    Widget D = std::move(Widget(20));    //error, 이동 생성자는 삭제되었습니다.
}
cs



<override>

부모 클래스에서 virtual로 선언한 함수를, 자식 클래스에서 재 정의할때 사용하는 키워드 입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A {
public:
    virtual void I();
    virtual void MY() const;
    virtual int ME(int i = 0);
    void MINE();
};
class B : public A{
public:
    virtual void I() override;    //ok
    virtual void MY() override;    //error, 상수화 되지 않았습니다.
    virtual int ME(int i = 0) override;    //ok, 인자까지 동일합니다.
    void MINE() override;    //error, virtual 함수가 아닙니다.
};
cs

사실 쓰다보니 이동 생성자 부분이 왜 안들어가나... 고민하다가 1시간만에 작성할 글을 3시간이나 걸린 글...
다음글은 C++14에 대한 내용과 STL 글 작성