LastMod:

👨‍💻 개인 공부 기록용 블로그 입니다.
💡 틀린 내용이나 오타는 댓글, 메일로 제보해주시면 감사하겠습니다!! (__)

42 Cursus - Cpp Module을 진행하기 위해 정리했었던 C++98 기본 개념들로, C++11, C++14와 다른 내용이 있을 수 있습니다.
각 개념은 정리한 시간 순으로 배치되어 있으며, 한 포스트에 배치된 개념이 크게 관련이 없을 수 있습니다.

함수 오버로딩 (Function Overloading)

  • 다형성의 구현 중 하나로, 같은 일을 처리하는 함수를 매개변수의 형식을 조금씩 달리하여 하나의 이름으로 중복 정의할 수 있게 해주는 것.
  • 함수 시그니처 (Function Signature)가 같다면, 동일한 함수의 재정의로 취급되어 컴파일 오류가 발생한다.
    • 함수 시그니처는 쉽게 이야기하여 함수를 구분하기 위한 구성 요소이다.
    • 함수의 이름, 매개변수의 개수, 매개변수의 자료형 등이 구분 대상이다.
    • 리턴 타입은 함수 시그니처에 해당하지 않는다. ⇒ 함수 호출시에, 컴파일러가 리턴 타입을 구분할 수 없기 때문이다. (함수를 호출할 때 func(arg); 형태로 사용하는 것을 생각해보면 된다.)
  • C++은 오버로딩된 함수의 모호한 호출 (Ambiguous Function Call)을 허용하지 않는다.
void Display(int x, int y); // 1. integer 2개를 인수로 받는 출력 함수
void Display(double x, double y); // 2. double 2개를 인수로 받는 출력 함수

Display(4, 2); //1.을 호출한다.
Display(4. , 2. ); //2.를 호출한다.
Display(4, 2.); //1. 과 2. 둘 다 해당될 수 있다. -> 모호한 호출이므로, C++ 컴파일러는 이를 오류로 잡아낸다.

연산자 오버로딩 (Operator Overloading)

  • 다형성의 구현 중 하나로, 하나의 연산자를 여러 의미로 사용할 수 있도록 해주는 것.
  • 연산자 오버로딩을 클래스까지 확장할 수 있다.
  • 연산자를 오버로딩하기 위해서, 연산자함수 라는 개념을 이용한다. operator{op}(argument);
  • 클래스의 멤버함수로써 정의할 수 있으며, 전역 함수로도 정의할 수 있다.

friend 키워드

  • 연산자 오버로딩을 전역 범위로 정의했을 때, 클래스의 private 멤버에 접근할 수 없다는 단점이 생긴다.
  • 이를 해결하기 위해, 해당 함수를 클래스내에 friend 키워드를 붙인 채로 프로토타입을 위치시키면, 해당 클래스의 private에 접근할 수 있는 권한이 생긴다.
  • 즉, friend 키워드는 해당 함수를 어떠한 객체의 private에 접근할 수 있게 해주는 지정자의 역할을 한다.

L-value와 R-value

  • 본래 Lvalue와 Rvalue는 각각 Left-Value 와 Right-Value로 풀어서 썼었다.
  • Lvalue는 표현식에서 왼쪽과 오른쪽 모두에 올 수 있는 값이고, Rvalue는 표현식에서 오른쪽에만 올 수 있는 값으로 통칭해서 사용했었기 때문이다.
    • 실제로 C표준에서는 위와 같이 정의한다.
  • C++ 표준에서는, Lvalue를 단일 표현식 이후 사라지지 않는 객체(즉, 메모리 주소를 가짐), Rvalue를 표현식이 종료된 이후 존재하지 않는 임시 객체(즉, Lvalue가 아니면 모두 Rvalue임)로 정의한다.
// 밑줄이 있는 것은 모두 Rvalue이다.
int x = 3;
const int y = x;
int z = x + y;
int* p = &x;

cout << string("one");

++x;
x++;
  • 주소 연산자&는 항상 Lvalue를 요구한다. 레퍼런스 또한 마찬가지이다. 하지만, C++11부터 &&를 통해 Rvalue Reference를 사용할 수 있다.
  • 마지막 두 개의 증가 연산자에 대한 동작을 알아보자.
    • 전위 증감 연산은 피연산자에 대한 증감연산을 진행 후, 그 피연산자의 메모리를 참조하는 Lvalue를 리턴한다.
    • 후위 증감 연산은 피연산자에 대한 증감연산을 진행 후, 연산을 진행하기 전의 값인 Rvalue를 리턴한다.
    • 따라서 ++(++n) 과 같은 형태는 가능하지만(Lvalue에 대한 두 번 증가), (n++)++은 불가능(Rvalue에 대한 연산 불가)하다.

증감연산자의 오버로딩

  • 위에 있는 증감 연산자의 동작에 의하여, 오버로딩할 때도 약간 다르게 정의해줘야 한다.
class Point
{
private:
    int xpos, ypos;
public:
 
    // 전위증가
    Point& operator++(void)
    {
        xpos += 1;
        ypos += 1;
        return *this;
    }
 
    //후위증가
    const Point operator++(int)
    {
        const Point retobj(xpos, ypos);
        xpos += 1; ypos += 1;
        return retobj;
    }
};
  • 전위 증감은 Lvalue를 리턴해줘야 하기 때문에, 연산 후 객체 자신의 레퍼런스를 리턴한다.
    • 이터레이터의 경우, 임시 객체에 대한 추가 할당 연산이 없기 때문에 일반적으로 cost가 후위 증감에 비해 낮다.
  • 후위 증감은 Rvalue를 리턴해줘야 한다. 따라서 객체 자신에 대한 연산 전에, 임시 객체를 하나 만들고 그것을 리턴한다.
    • 따라서 레퍼런스를 리턴하지 않으며, const를 통해 더 이상의 연산이 불가하도록 한다.
  • 두 연산의 인자가 다른 이유는 그저 컴파일러가 구분할 수 있도록 도와주는 용도이다.
    • 함수 오버로딩의 함수 시그니처 설명 항목을 보면 알겠지만, 컴파일러는 리턴값으로 함수를 구분할 수 없기 때문에, 인자로 dummy값을 주어 구분하게 하려는 용도이다.

Reference

CPP 레퍼런스 (함수 오버로딩)

CPP 레퍼런스 (연산자 오버로딩)

CPP 레퍼런스 (오버로딩 해결)

CPP 레퍼런스 (Value Category)

Lvalue 와 Rvalue 정리

Leave a comment