# \[CPP-02] Canonical 클래스 복사 생성자와 대입 연산자 오버로딩

title: "\[CPP-02] Canonical 클래스: 복사 생성자와 대입 연산자 오버로딩" description: "canonical이란, "규정대로"하는 프로그래밍을 의미한다. 앞으로의 CPP 과제는 Coplien form 규정대로 클래스를 작성해야한다." date: 2021-04-25T02:45:49.606Z

## tags: \["42Seoul","Canonical","Coplien","cpp","대입 연산자 오버로딩","복사 생성자"]

**\[CPP-Module/ex00: My First Canonical Class 과제]** You are going to discover a new and awesome number type: `fixed point numbers`! Write a **canonical** `Fixed` class to represent fixed point numbers:

Private members:

* `int` : 고정 소수값을 저장
* `static const int` : 항상 8 글자의 값을 가지는 fractional bits가 저장

Public members:

* default 생성자&#x20;
* 소멸자
* 복사 생성자
* 대입 연산자 오버로딩
* getRawBits(void) const
* setRawBits(int const raw)

## 1. Canonical 클래스

> From now on, each class **you write MUST be in canonical (Coplien) form**: At least one default constructor, a copy contructor, an assignation operator overload and a destructor. We won’t ask again.

`canonical`이란, "규정대로"하는 프로그래밍을 의미하며, 반대로 `non-canonical`이란 "규정에 따르지 않고"하는 프로그래밍을 의미한다. 일종의 코딩 컨벤션같다. 앞으로는 과제를 진행할 때 `Coplien form` 규정대로 클래스를 작성해야한다.

1. 디폴트 생성자
2. 복사 생성자
3. 할당 연산자 오버로딩
4. 소멸자

위 네 가지 멤버는 그것이 사용되던 안되던 항상 클래스에 정의되어야 한다. 생성자와 소멸자는 알겠는데 `복사 생성자`와 `대입 연산자 오버로딩`은 생소한 개념이다.

## 2. 복사 생성자

```cpp
Fixed::Fixed(const Fixed& fixed) //선언
```

```cpp
Fixed a;
Fixed b(a); // a를 복사해 b를 생성했다.
```

상수로 선언된 참조자이며, type은 클래스(자기 자신)가 된다. 복사 생성자는 기존 값을 복사해 전달해주는 개념이라 값이 바뀌어서는 안된다. 따라서 `const`로 선언한다.

* 정의하지 않으면 **디폴트 복사 생성자**가 생긴다.
* 디폴트 복사 생성자는 `얕은 복사`를 한다.&#x20;
* 복사생성자를 새로 정의했다고해서 다 `깊은 복사`인 것은 아니다. `깊은 복사`를 하고 싶다면 복사 생성자 내에 **동적 할당**된 새 객체가 있어야 한다.&#x20;

### 2.1. 얕은 복사(Shallow Copy)

* 객체를 복사할 때, 해당 객체만 복사하여 새 객체를 생성한다.
* 복사된 객체의 인스턴스 변수는 원본 객체의 인스턴스 변수와 같은 메모리 주소를 참조한다.
* 따라서 해당 메모리 주소의 값이 변경되면 원본 객체 및 복사 객체의 인스턴스 변수 값은 같이 변경된다.

### 2.2. 깊은 복사(Deep Copy)

* 객체를 복사 할 때, 해당 객체와 인스턴스 변수까지 복사하는 방식.
* 전부를 복사하여 새 주소에 담기 때문에 참조를 공유하지 않는다.

## 3. 대입 연산자 오버로딩

### 3.1. 오버로딩이란?

CPP에는 `오버로드`라는 개념이 존재한다. C와는 다르게, 함수의 이름이 같아도 매개변수가 다르면 선언 및 정의가 가능하다. 지금까지 이 개념을 이용해 조건에 따라 다른 기능을 하도록 생성자를 여러개 만들 수 있었던 것이다. 즉 오버로드는, 같은 객체지만 다른 기능을 하도록 만든다는 의미다.

그렇다면 연산자 오버 로딩이란 기존의 연산자를 재정의하는 것이라고도 볼 수 있다. 원래 객체끼리는 `+` 같은 연산자를 사용할 수 없는 것이 일반적이다. 하지만 연산자 오버로딩을 통해 `+`의 기능을 재정의 해준다면, 객체가 기본 자료형 변수처럼 덧셈, 뺄셈, 혹은 곱셈과 같은 연산들을 할 수 있도록 만들 수 있다. 이렇게 되면 객체도 기본 자료형 데이터처럼 취급하는 것이 가능하다.

### 3.2. 연산자 오버로딩 방법

```cpp
Fixed& operator=(const Fixed& fixed);
```

연산자 오버 로딩을 하기 위해서는 **operator** 키워드와 **연산자**를 묶어서 함수의 이름을 정의하면, 연산자를 이용한 함수의 호출이 가능하다.

위 예제처럼 클래스 내부에 연산자 오버로딩을 선언하면, 클래스 멤버 함수에서 오버로딩된 `=` 을 사용할 수 있다.

예를 들어 아래 복사 생성자

```cpp
Fixed::Fixed(const Fixed& fixed)
{
    std::cout << "Copy constructor called" << std::endl;
    *this = fixed;
}
```

`*this = fixed;` 는 왼쪽의 `*this` 객체를 대상으로 `operator=` 함수를 호출하면서 오른쪽 피연산자인 객체 `fixed`를 인자로 전달한다는 의미를 가진다.

* 정의하지 않으면 **디폴트 대입 생성자**가 생긴다.
* 디폴트 대입 생성자는 `얕은 복사`를 한다.
* 대입 생성자 내에서 **동적 할당**을 하거나 깊은 복사가 필요하면 **직접 정의**를 해야 한다.

### 3.4. 연산자 오버로딩 호출 시점

아니 그러면, 이 프로그램 내의 모든 코드에서 `=` 연산이 재정의 되는건가? 하는 의문을 가질 수 있다. 원래 기능인 대입 연산도 필요하고, 객체를 복사할 때도 `=` 가 필요한데 말이다.

사실, 복사 생성자와 대입 연산자 오버로딩은 유사하지만 호출되는 시점이 다르다. `복사 생성자`는 객체가 새로 생성되는 시점에서 대입을 할 때 호출이 되고 `대입 연산자 오버로드`는 객체 두 개가 이미 생성 및 초기화 진행된 상태에서 **올바른 인자를 대입** 할 때 호출이 된다.

즉, 연산자 오버로딩 함수의 매개변수를 특정 객체로 선언했다면, `=` 연산자 오른쪽에 그 객체가 들어왔을 때만 연산자가 오버로드 된다.

### 3.4. 주의점

연산자 내에 동적 할당이 된 경우에는 이전에 동적 할당되었던 데이터를 해제해야 한다. 그렇지 않으면 기존에 있던 데이터가 소멸되지 않아 메모리 누수가 생긴다.

## 4. 정리

```cpp
int main(void)
{
    Fixed a;
    Fixed b(a);
    Fixed c;

    c = b;

    std::cout << a.getRawBits() << std::endl;
    std::cout << b.getRawBits() << std::endl;
    std::cout << c.getRawBits() << std::endl;

    return 0;
}
```

```cpp
//console
Default constructor called //a 생성
Copy constructor called // b 생성
Assignation operator called // *this = fixed;
getRawBits member function called // fixed.getRawBits();
Default constructor called //c 생성
Assignation operator called //c = b
getRawBits member function called // fixed.getRawBits();
getRawBits member function called
0
getRawBits member function called
0
getRawBits member function called
0
Destructor called
Destructor called
Destructor called
```
