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

2025/03/28 ch.15 연산자 오버로딩

by 딴짓거리 2025. 3. 28.

double()을 구현했을 때의 문제점

SpreadsheetCell cell { 6.6 };
double d1 { cell + 3.3 }; //컴파일 에러!

이유는?

cell이 double() 을 통해 형변환 되어 덧셈이 될지

3.3이 double 생성자를 통해 임시객체가 되어 덧셈이 될지

컴파일러가 정할 수 없기 때문

 

생성자 앞에 explicit 키워드를 지정해서 자동 형변환이 일어날때 이 생성자를 사용하지 않게 할 수 있다

다만 형변환으로 객체가 생성되는 기능은 매우 편리하기 때문에 막기엔 좀 그렇다

그래서 C++11 부터는 double 변환 연산자를 explicit으로 선언해서 생성자쪽으로 연산되도록 할 수 있다

 

15.8.3 부울 표현식으로 변환

객체를 부울 표현식에서 사용하면 좋을 때가 있다

if(obj) ...
if(!obj) ...
operator void*() //앞선 포인터 객체에선 포인터를 직접 반환하게 하면 됨
operator bool() // 이렇게 해도 됨
if (p != nullptr) //에러

p 객체가 bool() 오버로딩이 되어있더라도 nullptr은 0(false) 가 아니기 때문에 당연히 에러

 bool operator!=(const Pointer<T>& lhs, std::nullptr_t rhs); // null_ptr에 대한 비교 수행 가능

if (p != NULL) //에러!

null_ptr은 0이 아니지만 

0은 null_ptr 취급될 수 있다

NULL은 0또는 0L(null)로 취급되므로 위 조건식은 p != 0으로 취급된다

그러므로 0을 정수 0으로 취급할지 null_ptr로 취급해서 오버로딩한 !=를 타야 할지 컴파일러는 알 수 없다

 

bool()을 정의하면 생기는 또 다른 문제

Pointer<SpreadsheetCell> anotherSmartCell { new SpreadsheetCell { 5.0 } };
int i { anotherSmartCell };

int i에 대입할때 형변환이 일어나는데 bool도 어쨋든 0,1의 int값이 될 수 있으므로 정의된 bool() 오버로드를 먼저 타버림

그리고 int로 형변환이 일어나 결론적으로 i에는 0 또는 1 값밖에 안들어감

 

그래서 다들 void* 오버로드를 선호함

 

15.9 메모리 할당 및 해제 연산자 오버로딩

작은 객체를 여러번 할당 해제하는 과정에서 발생하기 쉬운 메모리 파편화를 방지하기 좋음

new 할때마다 메모리를 할당하는 대신 미리 만들어놓은 메모리 풀에서 객체를 꺼내 쓰도록 구현할 수 있음

 

new 표현식

operator new를 호출해서 객체에 대한 메모리 공간을 할당 -> 객체의 생성자 호출

-> 객체에 대한 포인터 리턴

 

delete 표현식

cell 소멸자 호출 -> operator delete 호출 -> 메모리 해제

 

new delete를 오버로드 하면 메모리 할당과 해제 과정을 직접 제어할 수 있지만 표현식 자체를 오버로드 할 수는 없다.

즉 실제로 메모리를 할당하고 해제되는 과정은 커스터마이즈 할 수 있지만 생성자와 소멸자를 호출하는 동작은 변경 불가!

 

 

new 오버로딩 6종류

전부 다른 new 오버로드니 주의

 

# new(nothrow) <- new로 할당하고 실패하면 예외 대신 nullptr 던짐

 

나머지 두개는 할당 작업 없이 기존에 할당된 객체의 생성자만 호출하는 특수한 형태의 new

배치 new 연산자

기존에 확보된 메모리에서 객체를 생성 가능

하지만 이 둘은 오버로딩이 금지됨

 

delete의 6가지 오버로딩

 

delete는 사용하는 방식이 사실 delete delete[] 두가지 뿐. delete (nothrow)는 없기 때문

c++에서 delete가 실패했을 경우 어떻게 되는지에 대한 정의가 없기 때문에 절대로 예외를 던지면 안되는 연산임

 

하지만 오버로딩은 6가지임

delete 오버로딩의 nothrow 버전은 생성자에서 익셉션이 발생할 때만 사용됨

배치 버전의 delete도 아무런 작업 X. 배치 버전의 new가 애초에 메모리 할당을 하지 않기 때문

 

15.9.2 글로벌 버전의 new delete 오버로딩

프로그램 자체에서 new delete를 재정의

클래스에서 이보다 구체적인 오버로딩이 되어있으면 그 버전을 호출

# 쓰지마!!

 

new를 재정의 했다면 반드시 그에 대응되는 delete도 재정의 해야한다

 

메모리 할당의 일관성을 유지하기 위해 new 한종류를 오버로딩 하면 다른 6가지 모두 오버로드 해주는게 좋다

 

명시적으로 원하지 않는 버전의 new 오버로드를 막을 수도 있고

 

new에 매개변수를 받도록 오버로드 할 수도 있다

 

15.10 사용자 정의 리터럴 연산자

 

이건 좀;;

 

 

823p