Post

(C++) operator overloading

toString 같은, 문자열 반환 오퍼레이터

두 가지가 있고, 두개 모두 오버로딩하는게 좋다.

1. string cast operator

캐스트 연산자를 이용한 한 방법이다. 다음과 같은 상황에서 자동으로 string으로 캐스팅해서 반환해준다.

1
std::string mac\_str = src\_mac\_addr.toString()

C스타일의 출력(printf)을 사용하는 경우에도 이걸 이용해서 string으로 반환 받은 다음 .c_str()을 써서 변환해서 사용하는 식으로도 쓰인다.

1
2
3
4
5
6
7
class Foo {
public:
    operator std::string() const { return "member to string!"; }
};

Foo foo;
std::cout << foo; // Will print "I am a foo!".
2. operator«

operator<<를 오버로딩 할 때 주의해야 할 점은, 이게 클래스의 멤버이면 안된다는 점이다. 따라서 friend로 선언하거나 아예 전역 함수로 빼야 한다. 전역 함수로 빼도 파라미터 타입으로 구분할 수 있으니 둘 중 뭘 써도 상관 없고 이건 취향 차이인 듯? friend로 선언하면 클래스의 private, protected 멤버에도 접근할 수 있다는 장점은 있다. 이 오버로딩의 장점은, cout<<이나 clog<<등 어떤 스트림에든 그냥 연결해서 사용하면 된다는 점이다.

1
2
3
4
5
6
7
8
9
10
friend std::ostream& operator<<(std::ostream& os, const MacAddr& obj) { 
    return os << std::hex << std::setfill('0') <<
                    std::setw(2) << unsigned(obj.addr[0]) << ":" <<
                    std::setw(2) << unsigned(obj.addr[1]) << ":" <<
                    std::setw(2) << +obj.addr[2] << ":" <<  // +나 unsigned()나 같다.
                    std::setw(2) << unsigned(obj.addr[3]) << ":" <<
                    std::setw(2) << unsigned(obj.addr[4]) << ":" <<
                    std::setw(2) << unsigned(obj.addr[5]) <<
                std::dec << std::setfill(' ');  // 시작할 때 설정했던걸 다시 풀어줘야 이후에 오는 stream에 영향이 안감.
}

const를 붙여주기를 권장한다. map<MacAddr, ValueCls> 이렇게 사용하는 경우, 맵의 키로 들어가는 요소에 다음과 같이 접근하는 경우 자동으로 const로 간주하기 때문에, const가 없다면 다음 에러가 발생한다.

1
2
src/main.cpp:111:31: error: binding reference of type MacAddr& to const MacAddr discards qualifiers
         std::cout << iter->first;

객체 생성과 동시에 대입하는건, 생성자와 operator=이 둘 다 정의되어 있어야 한다.

생성자를 정의하지 않았을 때, 다음 코드에서 이런 에러가 발생한다. (직접 MacAddr mac(_mac)을 집어넣었을 때는 이와 다른 에러가 발생하기는 한다. no matching function.)

1
2
3
test.cpp:39:19: error: conversion from uint8_t* {aka unsigned char*} to n
on-scalar type MacAddr requested
     MacAddr mac = (uint8_t*)_mac;

생성자가 없는 경우에는 일단 객체를 만들고 나서 대입하면 잘 된다.

1
2
MacAddr mac;
mac = (uint8\_t\*)\_mac;

생성자를 다음과 같이 정의하면 선언과 동시에 초기화 가능하다.

1
2
3
MacAddr(uint8\_t \*target) { memcpy(this->addr, target, LENGTH); }
...
MacAddr mac = (uint8\_t\*)\_mac;

* 물론 operator=도 정의되어 있어야 동작한다.

This post is licensed under CC BY 4.0 by the author.