본문 바로가기
전문가를 위한 C++

2025/03/06 ch.09 클래스와 객체 완전 정복

by 딴짓거리 2025. 3. 6.

 

9.1 프렌드

클래스 안에서 다른 클래스 , 다른 클래스의 멤버 함수, 비 멤버함수를 선언하는 기능

 

9.1 프렌드

 

클래스 안에서 다른 클래스 , 다른 클래스의 멤버 함수, 비 멤버함수를 선언하는 기능

프렌드로 선언한 대상은 이 클래스의 protected나 private 데이터 멤버와 메서드에 접근할 수 있다.

일반적인 독립함수도 필요하다면 프렌드로 지정해서 객체의 멤버 변수에 쉽게 접근할 수 있도록 만들 수 있다.

 

이러한 프렌드는 캡슐화 원칙에 위배될 수 있으므로 남용하지 맙시다

 

9.2.3 복제와 대입 처리하기

복제 생성자나 데입 연산자를 직접 정의하지 않으면 컴파일러가 자동으로 만들어준다

일반적으로 기본타입에 대해서는 비트 단위 복제(얕은 복제)나 데입이 적용된다.

즉, 원본 객체의 데이터 멤버를 대상 객체로 단순히 복제하거나 대입하기만 한다.

하지만 독적으로 할당한 메모리가 있으면 문제가 발생한다.

-> 포인터가 가리키는 곳의 데이터를 복사해와야 하지만 얕은 복사로는 포인터만 복사해온다

이런 상태에서 원본 객체가 소멸되면? 댕글링 포인터 발생

# 복제 생성자와 대입 연산자는 반드시 깊은 복제를 적용해야 한다.

포인터 데이터 멤버뿐만 아니라 포인터가 가리키는 실제 데이터를 복사해옵시다.

 

익셉션에 대한 안정성을 높인 대입 연산자 구현하기

1. 임시 복제본을 만든다. <- 원본 객체는 변경하지 않으므로 익셉션이 발생하도 문제X

2. swap() 함수를 이용하여 현재 객체를 생성된 임시 복제본으로 교체한다.

3. 임시객체를 제거한다.

 

클래스에서 메모리를 동적으로 할당하면 대입이나 복제에서 문제가 발생할 가능성이 크다

기본적으로 생성되는 복제 생성자와 대입연산자를 delete 해버리는것도 좋다

 

9.2.4 이동 의미론

이동 생성자와 이동 대입 연산자를 정의해야 함

리소스의 소유권을 다른 객체로 이동시킨다

 

원본 객체에 있는 데이터 멤버를 새 객체로 이동시킨다

-> 원본객체는 정상이긴 하나 미확정인 상태로 남는다. 널값으로 초기화 시켜주는게 좋긴 함

 

우측값 레퍼런스

좌측값 - 이름 있는 변수처럼 주소를 가질 수 있는 대상을 가리킴. 주로 대입문의 왼쪽에 나오기 때문

우측값 - 리터럴, 임시 객체, 값. 좌측값이 아닌 나머지. 주로 대입문의 오른쪽에 나옴

 

우측값 레퍼런스?

우측값이 임시 객체거나 move()로 명시적으로 이동된 객체일때 적용되는 우측값에 대한 레퍼런스

함수의 매개변수에 &&를 붙여서 우측값 레퍼런스로 만들 수 있다.

일반적으로 임시객체는 const type&으로 취급하지만 우측값 레퍼런스로 사용하는 것이 있다면 그 버전으로 임시 객체를 처리

어쨋든 복사를 한번이라도 줄이기 위해..

 

우측값 레퍼런스를 매개변수로 받는 함수에 좌측값을 전달하면 에러가 난다

move()로 좌측값을 우측값으로 캐스팅해서 전달해야 한다.

 

# 우측값 레퍼런스 매개변수와 같이 이름 있는 우측값 레퍼런스는 타입이 우측값 레퍼런스일 뿐 이름이 있으므로 매개변수 자체는 좌측값임에 주의

 

변수를 우측값 레퍼런스로 선언해서 값을 할당할 수도 있다.

 

클래스에 이동 의미론을 추가하려면 이동 생성자와 이동 대입 연산자를 구현해야 함

이 둘을 noexcept로 지정해서 두 메서드에서 익셉션을 절대로 던지지 않는다고 컴파일러에 알려줘야 표준 라이브러리와 호환성을 유지할 수 있다. 표준 라이브러리는 이동 의미론을 구현하고 익셉션도 던지지 않는다고 보장해야 저장된 객체를 이동시키기 때문

# 이동 의미론은 원본 객체가 더 이상 필요 없어서 삭제할 때만 유용함

 

클래스에 소멸자, 복제 생성자, 이동 생성자, 복제 대입 연산자, 이동 대입 연산자 등과 같은 특수 멤버 함수를 하나 이상 선언했다면 일반적으로 이들 모두를 선언해야 함. <- 5의 법칙

 

std::exchange()

기존 값을 새 값으로 교체한 후 기존 값을 리턴한다.

 

 

~454.p