ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Effective C++] 5. C++이 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자
    C_C++ 프로그래밍/Effective C++ 2019. 9. 5. 19:18

     

         로재의 개발 일기      

    C++이 자동으로 만들어주는 함수

    우리들이 만드는 거의 모든 C++ 클래스에 한 개 이상 들어있는 것이 무엇일까요?

    바로 <생성자>  <소멸자>  <대입 연산자> 입니다.


    하지만 다들 경험 해보셨을텐데요.

    딱히 작성을 하지 않아도 컴파일러가 알아서 작성을 해줍니다.


    하지만 이 부분에서 의도치 않은 오류가 있을 수 잇습니다.


    이번 항목은 그것을 말하고 있습니다.


      자동으로 생성이 된다

    빈 클래스를 작성을 해보았습니다.

    이 클래스를 호출하고 복사 대입하는 코드를 구현해봤어요.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    /*
     * Made person: rojae
     * Made date: 2019.09.05
     * --- code description ---
     *  생성자, 소멸자, 복사 생성자가 저절로 생겨난다
     */
     
    #include<iostream>
     
    class Empty{
        // empty class 
    };
     
    int main(){
            Empty e1;
            Empty e2(e1);
            e2 = e1;
            return 0;
    }
     
    cs

    아무런 오류가 없는 것을 보아하니

    자동으로 생성이 되네요.


      직접 만들어서 봅시다.

    이미 구현되어 있다면

    자동으로 생성이 되지 않는 지 보겠습니다.

    당연히 생성은 되지 않습니다 ~

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    /*
     * Made person: rojae
     * Made date: 2019.09.05
     * --- code description ---
     *  생성자, 소멸자, 복사 생성자를 작성해보자
     */
     
    #include<iostream>
     
    class Empty{
            public:
                    Empty(){
                            std::cout << ".... Constructor .... " << '\n';
                    }
                    Empty(const Empty& rhs){
                            std::cout << ".... Copy constructor ...." << '\n';
                    }
                    ~Empty(){
                            std::cout << ".... destructor ...." << '\n';
                    }
                    Empty& operator = (const Empty& rhs){
                        std::cout << "Copy insert operator" << '\n';
                    }
    };
     
    int main(){
            Empty e1;
            Empty e2(e1);
            e2 = e1;
            return 0;
    }
     
    cs

    결과적으로 자동으로 생성되지 않고

    작성된 코드가 정상적으로 동작이 되었네요!

    ( 소멸자는 프로그램 종료와 함께 자동으로 호출되었습니다 )



    그렇다면 복사 생성자는 어떻게 만들어질까?

    자동으로 만들어지기도 하며 여러 개의 생성자 작성이 가능합니다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /*
     * Made person: rojae
     * Made date: 2019.09.05
     * --- code description ---
     *  복사 생성자는 어떻게 만들어지는가
     *  작성하지 않았기 때문에 아래와 같이 자동으로 생성된다
     *  NamedObject(const NamedObject& other) 
     *      : nameValue(other.nameValue), objectValue(other.objectValue) { }
     */
     
    #include<iostream>
     
    template<typename T>
    class NamedObject {
            public:
                    NamedObject(const char *name, const T& value);
                    NamedObject(const std::string& name, const T& value);
            private:
                    std::string nameValue;
                    T objectValue;
    };
     
    cs



    복사 생성자가 자동으로 생성되었다는 것을 알 수 있는 부분이였습니다.

    1
    2
    NamedObject(const NamedObject& other) 
           : nameValue(other.nameValue), objectValue(other.objectValue)
    cs




      자동으로 생성되지 않는 경우

    생성자가 자동으로 생성이 불가능 한 경우가 있습니다.

    1. 기반 클래스에서 private로 복사 대입 연산자를 생성한 경우
    파생 클래스에서 접근이 불가합니다
    2. 데이터 멤버가 상수 객체인 경우
    (const는 수정이 불가능 하기 때문에...)
    3. 참조자를 사용하는 객체를 복사 대입 하는 경우
    ( 다른 객체까지 영향이 미치기 때문에 컴파일이 거부됩니다 )


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    /*
     * Made person: rojae;
     * Made date: 2019.09.05
     * --- code description ---
     *  복사 대입 연산자가 자동으로 생성되지 않는 경우를 알아본다
     *  결과적으로 컴파일 오류가 발생하게 된다.
     *  참조자나 상수를 품고 있는 객체를 복사 대입하기 위해서는
     *  직접 복사 대입 연산자를 정의해야한다.
     */
     
    #include<iostream>
     
    template<class T>
    class NamedObject {
            public:
                    // 이 생성자는 이제 상수 타입의 name을 취하지 않는다. 
                    // nameValue가 비상수 string의 참조자가 되었기 때문이다. 
                    // 또한, 참조할 string을 가져야 하기 때문에 char* 는 없애 버렸다. 
                     NamedObject(std::string& name, const T& value);
                    // operator= 는 선언되지 않았다고 가정 
            private:
                             std::string& nameValue; // 참조자 
                             const T objectValue; // 상수 
    };
     
     
    int main(){
            std::string newDog("newDog");
            std::string oldDog("oldDog");
     
            NamedObject<int> nw(newDog, 2);
            NamedObject<int> old(oldDog, 22);
     
            nw = old;
            return 0;
    }
     
    cs


    >> 따라서 참조자나 상수 데이터 멤버를 갖고 있는 클래스의 경우에는 직접 복사 대입 연산을 작성해줘야 합니다.

    따라서 참조자나 상수 데이터 멤버를 갖고 있는 클래스의 경우

    출처: https://langgeek.tistory.com/entry/Effective-C-항목-5-C가-은근슬쩍-만들어-호출해-버리는-함수들에-촉각을-세우자 [랭긱의 블로그]



    마무리

    당연하지만 당연하지 않은 느낌

    ※ 본 글은 개인 포트폴리오 혹은 공부용으로 사용하기 때문에, 무단 복사 유포는 금지하지만, 개인 공부 용도로는 얼마든지 사용하셔도 좋습니다


    반응형
Designed by Tistory.