- 특별 멤버 함수 (Special member function)
특정 경우에 컴파일러가 자동으로 생성하는 클래스의 멤버 함수이다.
기본 생성자
값을 초기화하고 싶지 않을 때 편리하며 객체들의 배열을 만들 때는 무조건 사용한다.
혹 매개변수가 있는 생성자도 모든 매개변수에 디폴트 값을 제공한다면 기본 생성자가 될 수 있다.
단, 기본 생성자는 하나만 가질 수 있기 때문에 이러한 코드는 모호하여 불가능하다.
복사 생성자
디폴트 복사 생성자는 static멤버를 제외한 멤버들을 각 값으로 복사한다.(얕은 복사)
디폴트 복사 생성자: 사용자가 정의하지 않아서 C++가 자동으로 만든 복사 생성자
새로운 객체가 생성되어 같은 클래스의 객체로 초기화될 때마다 호출된다.
초기화될 때: 초기화는 새 개체를 선언하거나, 함수 인수를 값으로 전달하거나, 함수에서 값으로 반환할 때 발생합니다.
임시 객체를 생성할 때마다 복사 생성자를 사용한다.???
어셈블리어로 확인하면
MyClass d = MyClass(a); 같은 코드도
MyClass b(a)와 내부 동작이 완전히 같게 복사 생성자로 바뀌어 진행된다.
근데 공부하면서 헷갈렸었던 주의할 것이 있는데
MyClass d = MyClass(a) 부분처럼 대입 연산자(=)가 사용되었을 때
처리를 두 가지 방법으로 생각해 볼 수 있다.
1. 위의 어셈블리처럼 복사 생성자를 직접 사용하는 것
ex) "MyClass d = MyClass(a)" => "MyClass d(a)"
2. MyClass(a)부분에서 임시 객체를 생성하고 그 임시 객체를 대입 연산자로 d에 대입하는 두 단계로 나누어 처리
ex) MyClass d = 임시 객체
어떠한 방법을 따르는지는 컴파일러에게 달렸지만 복사 생성자는 항상 호출되고 대입 연산자가 있을 시에
대입 연산자가 호출될 수도 있다.
복사 할당 연산자(복사 대입 연산자)
위에서 객체 = 객체처럼 자연스럽게 대입 연산자를 사용했지만
이것 또한 우리가 정의하지 않아도 사용할 수 있었다, 그 이유는 C++에서 자동으로 만들어주었기 때문이다.
ms문서에 보면 초기화와 할당이라는 단어를 제일 먼저 설명해준다.
ms문서 : https://docs.microsoft.com/ko-kr/cpp/cpp/copy-constructors-and-copy-assignment-operators-cpp?view=msvc-170
그 이유는 초기화가 일어날 때는 복사 생성자, 할당이 일어날 때는 복사 할당 연산자가 실행되기 때문이다.
초기화: 초기화는 새 개체를 선언하거나, 함수 인수를 값으로 전달하거나, 함수에서 값으로 반환할 때 발생합니다.
할당: 한 개체의 값이 다른 개체에 할당되면(a = b) 첫 번째 개체(b)가 두 번째 개체(a)에 복사됩니다.
이렇게 초기화, 할당이라는 조건으로 나뉘어 실행되는 이유는 내가 생각하기에
새로 객체가 생성되지 않는 경우의 복사 상황에는 복사 생성자가 실행되지 않기 때문이라고 생각한다.
그래서 따로 처리가 필요한 것이고 그 처리가 복사 할당 연산자가 된 것이다.
초기화가 아닌 할당이 조건이라는 건,
복사받을 객체가 이미 초기화가 되어있고 어떤 값이 들어있다는 뜻이다.
이는 즉, 초기화(복사 생성자)와 달리
깊은 복사가 필요할 때 이전에 있던 동적 할당된 메모리를 필수로 해제시켜주어야 한다는 의미도 있다.
그리고 또 하나의 차이점으로 호출한 객체에 대한 참조를 리턴한다
(생성자는 리턴이 없음)
좀 더 쉽게 풀면 하나의 객체를 기존의 다른 객체에 복사할 때 사용된다고 볼 수 있다.
이 복사 생성자와 복사 대입 연산자 중 하나라도 정의한다면
나머지 하나를 정의해주는 게 좋다.
그래야 모든 복사 상황에 대해 정의가 되니 보다 코드의 의미가 명확해진다.
이동 생성자
이동 할당 연산자(이동 대입 연산자)
주소 연산자
기본 소멸자
모두 각각 정의되지 않았을 경우 자동으로 생성해 준다.
이동 관련은 뒤에 정리
- 얕은 복사, 깊은 복사
new에 의해 초기화되는 포인터들을 멤버로 가지고 있을 경우에
포인터 자체를 복사하는 것이 아니라, 그 포인터가 지시하는 데이터를 복사하는 복사 생성자를 정의해주면
그것을 깊은 복사라 한다.
얕은 복사와 깊은 복사라는 이름이 지어지게 된 이유는
얕은 복사는 포인터가 지시하는 데이터를 복사하기 위해 깊게 파고들지 않고, 그 주소만 얕게 복사하기 때문에
지어진 이름이다.
문제로 얕은 복사 시에는 하나의 할당된 메모리를 두 개의 객체가 멤버 변수로 접근하기에
하나의 객체가 소멸하여 소멸자를 호출하여 메모리가 해제될 때
다른 하나의 객체는 원하지 않아도 메모리가 해제된 포인터 멤버 변수를 가지게 되어 큰 문제가 될 수 있다.
그래서 깊은 복사를 해주는 것
- 복사 생성자 추가
위의 코드에서 String클래스와 string클래스는 생성자에서 내부적으로 동적할당이 일어난다.
그렇다면 Magazine클래스에 대한 복사가 일어날때 복사 생성자나 복사 할당 연산자를 정의해야하는가?
한다면 여기서는 아니다.
이유는 Magazine이 복사가 일어나면 디볼트 복사 생성자나 디폴트 복사 할당 연산자는
해당 멤버 자료형의 복사 생성자와 복사 할당 연산자를 사용하기 때문이다.
그렇기 때문에 String,string이 각각 복사 관련 함수를 깊은 복사로 잘 정의 했다면 정의한 복사 함수들이
실행되는것이다.
근데 같은 클래스가 아닌 다른 클래스에 대한 복사면 예외가 있다고 한다.
뒤에서 나온단다
'C++ > [책] C++ 기초 플러스' 카테고리의 다른 글
[ 822p ~ 892p ] static 멤버 함수, 생성자에 new 사용, 객체 참조형 리턴시 주의, 객체와 함께 사용하는 위치지정new (0) | 2022.03.26 |
---|---|
※[ 791p ~ 796p ] static 멤버 변수, in-class 초기화 (0) | 2022.03.25 |
[ 763p ~ 790p ] 하나의 매개변수를 취하는 생성자, explicit, 변환 함수, 변환과 프렌드 (0) | 2022.03.24 |
[ 727p ~ 762p ] 프렌드 멤버 함수 (0) | 2022.03.23 |
[ 709p ~ 726p ] 연산자 오버로딩, 연산자 함수, 오버로딩 제약 (0) | 2021.09.13 |