본문 바로가기

Dev/C++

operator overloading 2

operator->() #

"->"의 연산자 겹지정은 포인터를 리턴으로 하는 형식을 취하고 있으며
리턴되는 포인터의 "->" 연산자를 사용한 효과를 내는 특이한 특성을 가지고 있습니다.

이러한 형식으로 operator->() 함수의 리턴되는 포인터의 멤버를 제어할 수 있는 능력을 부여합니다.


#include <iostream>
#include <string>
using namespace std;

template <class T>
class SmartPtr
{
public:
        explicit SmartPtr(T *p = 0) : p_(p) {}
        virtual ~SmartPtr() { delete p_; }

        T* operator->() {
                return p_;
        }

        // 아래의 두개의 함수인 "!" 연산자 겹지정 함수와, 
           // bool 타입으로의 변환연산자 함수는 융통성있는 인터페이스로 인하여   
        // 문제가 생길 여지가 있으므로 사전에 그것에 대해 알고 사용 하셔야 
        // 미래의 재앙에서 벗어날수 있습니다. ^^ 
        bool operator!() {
                return p_ == null;
        }

        operator bool() {
                return p_ != NULL;
        }

private:
        // 복사생성자와 대입연산자를 사용 못하게 하기위해 
        // private 멤버로 두었으며 구현을 하지 않았습니다. 
        SmartPtr(const SmartPtr& sp);
        SmartPtr& operator=(const SmartPtr& sp);

private:
        T *p_;
};


int main()
{
        SmartPtr<string> sp(new string("ABCDEFG"));

        // T* operator->() 함수가 호출됩니다. 
        // 내부 string 객체의 length() 함수를 호출합니다. 
        cout << sp->length() << endl;

}

위는 간단한 Smart Pointer 예제입니다.

위의 "->" 연산자의 연잔자 겹지정 함수를 보면 T* 타입,
즉 main 함수의 코드 대로 라면 string* (stirng 타입의 template 이므로) 타입을 리턴하게 됩니다.

그럼 "->"의 겹지정함수는 다시 string* 타입에 대해 "->" 적용하여
string의 멤버에 접근 가능하게 만들어 줍니다.

만약 operator->()의 겹지정 합수에 리턴되는 포인터의 멤버를 제어할수 있는 능력이 없다면
아마 다음과 같이 코드를 작성해야 될것입니다.
cout << sp->->length() << endl;         // 혹은 
cout << (sp->)->length() << endl;   // 혹은 
cout << (*(sp->)).length() << endl;
하지만 위의 같은 코드는 컴파일 되지도 않을 뿐더라 인터페이스가 직관 적이지 못합니다.
그러한 이유료 C++에서는 operator->() 함수 즉 "->" 연산자 겹지정 함수에 대해 그러한
능력이 부여된 것입니다.


이러한 스마트 포인터는 C++의 표준 객체인 std::auto_ptr 부터 boost::shared_ptr,
ATL에서의 _com_ptr 객체 까지 두루두루 사용되고 있으며, 내부멤버를 접근을 제어를 위한
Proxy Pattern 같은 곳에서 유용하게 사용될 수 있는 기능입니다.




">>", "<<" 연산자 겹지정 #

">>" 혹은 "<<" 연산자는 비트단위의 Shift 연산을 하는 기능을 가지고 있는 연산자 입니다.
당연히 ">>" 혹은 "<<" 연산자도 사용자 정의 타입인 클래스 객체에 대해 Shift 연산을 수행하는
코드를 생성할수도 있고, 또한 다른 기능을 사용자 정의에 의해 구현될 수 있습니다.

그리고 또 하나의 의미인 스트림 객체의 in, out 기능에 해당하는 인터페이스를 매치 시킬수도 있습니다.

C++의 입문서중 가장 처음에 등장하는 코드인 Hello World 코드 입니다.

#include <iostream>
using namespace std;

int main()
{
        cout << "Hello World" << endl;
}
이 예제는 바로 cout 객체의 연산자 겹지정 함수인 operator<<()에 의해 가능해진 코드 입니다.

그럼 간단하게 C++의 "<<" 연산자 사용을 통하여 C++의 스트림 객체와의 융통성있는 인터페이스를
간단히 구현해 보자면 ...


#include <iostream>
#include <string>
#include <fstream>
using namespace std;


class Student
{
public:
        /* ... */

        string strName_;
        int nID_;
};


ostream& operator<<(ostream& os, const Student &st)
{
        os << st.strName_ << " " << st.nID_;
        return os;
}


int main()
{
        Student st;

        /* ... */

        // 파일스트림으로 st의 내용을 보냅니다. 
        ofstream fout("a.txt");
        fout << st;

        // 표준 출력 스트림으로 st의 내용을 보냅니다. 
        cout << st;
}
여기서 주의 할점은
ostream& operator<<(ostream& os, const Student &st)
와 같이 참조를 리턴해야 된다는 점입니다.
그래야 다음과 같은 코드가 가능해 집니다.
fout << "VALUE: " << st << " END" << endl;
그리고 위의 겹지정 함수는 Student에 멤버로도 작성이 가능합니다.
하지만 다음과 같은 문제점이 발생합니다.

class Student
{
public:
        /* ... */

        Student& operator<<(ostream& os) {
                os << strName_ << " " << nID_;
                return *this;
        }

        string strName_;
        int nID_;
};

int main()
{
        Student st;
        /* ... */

        ofstream fout("a.txt");

        // 관습적이지 못한 인터페이스 입니다. 
        // 사용하는 사람은 일관성 있는 인터페이스를 찾지못해 
        // 혼란에 빠질지도 모릅니다. 
        st << fout;
        st << cout;
}
그러므로 "<<" 와 같은 이항 연산자의 경우 friend 함수나 전역함수로 작성하는것이 관례입니다.
(표준 라이브러리는 대부분 전역함수로 작성되어 있습니다.)

전역함수로 연산자 겹지정을 구현 할때는 인자중 적어도 한개는 사용자 정의 타입이어야 합니다.
그러므로 다음과 같은 코드는 컴파일 에러 입니다.

// 사용자 정의 타입이 없습니다. oops~ 
double operator+(int n, double d)
{
        return n + d;
}

'Dev > C++' 카테고리의 다른 글

new 1  (0) 2008.05.01
initialization - array structure  (0) 2008.05.01
operator overloading 2  (0) 2008.05.01
operator overloading 1  (0) 2008.05.01
Conversion Functions - 변환함수  (0) 2008.05.01
explicit  (0) 2008.05.01