언어/C++

C++ (복사/이동) 생성자, 할당자, Rule of Three(Five)

tsyang 2020. 11. 8. 13:28

Cpp 의 클래스에는 기본 생성자 말고도,

 

소멸자, 복사 생성자, 복사 할당자, 이동 생성자, 이동 할당자가 존재한다. 

 

위의 다섯가지는 컴파일러가 알아서 만들어 주기 때문에 (기본 생성자 까지도), 일반적으로는 구현할 일이 없지만 만약 클래스에서 raw pointer 멤버 변수 등을 가지고 있어서 메모리 관리가 필요한 경우에는 위와 같은 생성/할당/소멸자를 프로그래머가 직접 구현해 줘야 한다. 

 

이동 생성/할당자를 뺀 세가지를 구현해야 한다는 법칙을 Rule of Three, 이동 생성/할당자를 포함하면 Rule of Five 라고 부른다.

 

그냥 코드에 주석달았음 

 

참고로 noexcept 같은 경우에는 성능상의 이점도 있다고 한다.

C++11 부터는 소멸자 등에 자동으로 컴파일러가 붙여준다고 함

class Cat
{
public:
    Cat()=default;   //생성자 하나라도 만들면 default constructor 안 만들어짐.
    				//이렇게하면 컴파일러가 만들어준거 쓰겠다는 뜻 
    Cat(std::string name, int age) : mName{std::move(name)}, mAge{age}
    {
    	std::cout << mName << "consturctor"<<std::endl;
    }
    ~Cat() noexcept // destructor, move assignment/constructor 에 붙여줘. 왜냐면 얘네는 새로운 리소스를 요청하지
    //않기 때문에 익셉션 뜰리가없고, 이걸 컴파일러가 보면 move를 확실하게 콜해줄 수 있음
    {
    	std::cout << mName << "desturctor"<<std::endl;
    }
    
//Copy constructor : 똑같은 object를 하나 생성하는 생성자
    //같은 클래스를 레퍼런스로 받으면 copy constructor
    Cat(const Cat& other) : mName{other.name}, mAge{other.mAge} 
    {
    	std::cout<< " copy constructor"<<std::endl;
        
        //다음과 같이 호출됨
        // Cat kitty2{kitty}; 
        
        //주의 : 다음의 경우는 assignment 아니다. copy construct다 
        // Cat kitty3 = kitty;
    }

//Move Constructor : 내부의 리소스를 넘김
    Cat(Cat&& other) noexcept : mName{std::move(other.mName)}, maAge{other.mAge}
    {
    	//만약 포인터를 썻다면
        //mPtr=other.mPtr;
        //other.mPtr=nullptr;
        
        
        //다음과 같이 호출됨
        //Cat kitty3 {std::move(kitty)};
    }
    
//copy assginment;
    Cat & operator = (const Cat& other)
    {
    	if(&other == this)
        {
        	return * this;
        }
    	mName = other.mName;
        mAge = other.mAge;
        return * this;
        
        //다음과 같이 호출됨
        //kitty = nabi 하면 복사가 된다. 
    }
    
//move assignment;
    Cat& operator=(Cat&& other) noexcept
    {
    	if(&other == this)
        {
        	return * this;
        }
    	mName = std::move(other.mName);
        mAge= other.mAge;
        return * this;
        
        //다음과 같이 호출됨
        //kitty = std::move(nabi);
    }
    
    void print()
    {
    	std::cout<<mName << " " <<mAge << std:;endl;
    }
    
private :
	std:: string mName;
    int mAge;
    //char * mPtr;
};

int main()
{
	Cat Kitty{"kitty", 1};
    Cat kitty2{kitty};
    Cat kitty3{std::move(kitty)};
    //메모리 구조가 어떻게 될까?
    //=> Kitty2,3은 값({"kitty", 1})을 갖고 Kitty는 아무것도 안 가리킴
    
}

//근데 mPtr을 안쓰면 컴파일러가 알아서 다 만들어주기 때문에 5개 다 지워도 됨.
    //Cat(const Cat& other) = delete; 이런식으로 컴파일러의 자동생성 막을수도있음
    //Cat() = delete; 이렇게 해주면 생성 자체를 못하게됨. (static 메서드같은경우)

'언어 > C++' 카테고리의 다른 글

Cpp 함수 (C++11 lambda, std::function)  (2) 2021.07.31
Cpp - 상속#2  (2) 2021.07.22
Cpp - 상속#1  (0) 2021.07.18
스마트 포인터  (0) 2020.11.08
L_value와 R_value  (0) 2020.10.11