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

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

1. string cast operator

캐스트 연산자를 이용한 한 방법이다.

다음과 같은 상황에서 자동으로 string으로 캐스팅해서 반환해준다.

```cpp

std::string mac_str = src_mac_addr.toString()

```

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


```cpp

class Foo {

public:

    operator std::string() const { return "member to string!"; }

};


Foo foo;

std::cout << foo; // Will print "I am a foo!".

```


2. operator<<

``cpp operator<<``를 오버로딩 할 때 주의해야 할 점은, 이게 클래스의 멤버이면 안된다는 점이다. 따라서 `` friend``로 선언하거나 아예 전역 함수로 빼야 한다. 

전역 함수로 빼도 파라미터 타입으로 구분할 수 있으니 둘 중 뭘 써도 상관 없고 이건 취향 차이인 듯?

`` friend``로 선언하면 클래스의 private, protected 멤버에도 접근할 수 있다는 장점은 있다.

이 오버로딩의 장점은, ``cpp cout<<``이나 ``cpp clog<<``등 어떤 스트림에든 그냥 연결해서 사용하면 된다는 점이다.

```cpp

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

```cpp

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.)

```cpp

test.cpp:39:19: error: conversion from ‘uint8_t* {aka unsigned char*}’ to n

on-scalar type ‘MacAddr’ requested

     MacAddr mac = (uint8_t*)_mac;

```


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

```cpp

MacAddr mac;

mac = (uint8_t*)_mac;

```


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

```cpp

MacAddr(uint8_t *target) { memcpy(this->addr, target, LENGTH); }

...

MacAddr mac = (uint8_t*)_mac;

```

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