250 likes | 439 Views
제 12 장 . 사용자 정의형으로서의 클래스. 학습 목표. 클래스를 기본형과 비슷한 방법으로 다룰 수 있게 하는 사용자 정의 형으로써의 기능을 알아본다. 클래스의 객체에 대해서 연산자를 사용할 수 있게 하는 연산자 오버로딩 기능에 대해서 알아본다. 연산자 오버로딩을 구현하기 위한 연산자 함수를 정의하는 방법과 주의 사항을 알아본다. 클래스 형을 특정 데이터 형으로 변환하는 변환 연산자를 구현하고 사용하는 알아본다. C++의 형 변환 연산자에 대해서 알아본다. 연산자 오버로딩.
E N D
제 12장. 사용자 정의형으로서의 클래스 프로그래밍언어및실습 (C++)
학습 목표 • 클래스를 기본형과 비슷한 방법으로 다룰 수 있게 하는 사용자 정의 형으로써의 기능을 알아본다. • 클래스의 객체에 대해서 연산자를 사용할 수 있게 하는 연산자 오버로딩 기능에 대해서 알아본다. • 연산자 오버로딩을 구현하기 위한 연산자 함수를 정의하는 방법과 주의 사항을 알아본다. • 클래스 형을 특정 데이터 형으로 변환하는 변환 연산자를 구현하고 사용하는 알아본다. • C++의 형 변환 연산자에 대해서 알아본다. 프로그래밍언어및실습 (C++)
연산자 오버로딩 • 기본형에 대해서 사용하는 연산자의 의미를 확대해서 클래스 형에 대해서도 사용할 수 있도록 만드는 기능 프로그래밍언어및실습 (C++)
연산자 오버로딩의 필요성 class String { ... public: bool IsEqual(const String& s); // 문자열의 내용이 같으면 true를 리턴하는 함수 }; template <typename T> void f(const T& x, const T& y) { if( x == y ) // T 형의 두 변수의 값을 비교한다. ... } int main() { int a = 10, b = 10; f<int>(a, b); // T가 int 형일 때 템플릿의 인스턴스화 OK String s1("abc"), s2("abc"); f<String>(s1, s2); // T가 String 형일 때 템플릿의 인스턴스화 실패 } 프로그래밍언어및실습 (C++)
연산자 함수 • "operator 연산자"의 형식으로 정의되는 함수 • 만일 연산자 함수가 정의 되지 않았으면 클래스의 객체에 대해 연산자를 사용하는 것은 컴파일 에러가 된다. 프로그래밍언어및실습 (C++)
연산자 함수의 정의 (1) • 이항 연산자를 연산자 함수로 정의할 때는 • 피연산자 중 좌변이 객체가 되고, + 연산자가 operator+라는 멤버 함수가 되고, 나머지 피연산자인 우변은 해당 함수의 인자가 된다. 프로그래밍언어및실습 (C++)
연산자 함수의 정의 (2) class String { ... public: String operator+(const String& s) const; }; String String::operator+(const String& s) const { char* tmp = new char[Size() + s.Size() + 1]; ::strcpy(tmp, m_str); ::strcat(tmp, s.m_str); String result(tmp); delete [] tmp; return result; } 프로그래밍언어및실습 (C++)
연산자 함수의 구현 • 연산자 함수의 구현 • 클래스의 멤버 함수로 정의하는 방법 • 멤버 함수가 아닌 전역 함수로 정의하는 방법 • 이항 연산자 오버로딩 • 연산자 함수를 클래스의 멤버 함수로 정의하면 피연산자 중 하나가 클래스의 객체가 되므로 연산자 함수의 인자의 개수는 하나가 된다. • 연산자 함수를 전역 함수로 정의하면 피연산자가 모두 연산자 함수의 인자가 되므로 연산자 함수는 두 개의 인자를 갖게 된다. 프로그래밍언어및실습 (C++)
멤버 함수로 정의 (1) • 이항 연산자의 경우 피연산자 중 좌변을 객체로 잡으면, 연산자가 객체가 속한 클래스의 멤버 함수가 되고, 남은 피연산자가 연산자 함수의 인자가 된다. • 연산자 함수를 정의할 때 연산자 함수의 리턴형은 연산의 결과에 따라서 결정된다. 프로그래밍언어및실습 (C++)
멤버 함수로 정의 (2) class String { ... public: bool operator==(const String& s) const; bool operator!=(const String& s) const; }; bool String::operator==(const String& s) const { return ::strcmp(m_str, s.m_str) == 0 ? true : false; } bool String::operator!=(const String& s) const { return ::strcmp(m_str, s.m_str) ? true : false; } 프로그래밍언어및실습 (C++)
전역 함수로 정의 (1) • 피연산자 중 좌변이 객체가 아니라면 연산자 함수를 전역 함수로 정의한다. • 연산자의 피연산자 수와 연산자 함수의 인자의 개수가 항상 같다. 프로그래밍언어및실습 (C++)
전역 함수로 정의 (2) class String { ... friend String operator+(const char* s1, const String& s2); }; String operator+(const char* s1, const String& s2) { char* tmp = new char[::strlen(s1) + s2.Size() + 1]; ::strcpy(tmp, s1); ::strcat(tmp, s2.m_str); String result(tmp); delete[] tmp; return result; } 프로그래밍언어및실습 (C++)
삽입 연산자 오버로딩 • 출력에 사용되는 << 연산자를 오버로딩하려면 전역 함수로 정의한다. 프로그래밍언어및실습 (C++)
연산자 오버로딩의 일반적인 규칙 • 연산자 오버로딩을 할 때 새로운 연산자를 정의할 수는 없다. 예를 들어 ‘#’을 새로운 연산자로 정의하는 것은 잘못이다. • 연산자의 우선 순위, 피연산자의 개수, 결합 방향 등을 변경할 수 없다. • 기본형에 대해서는 연산자 오버로딩할 수 없다. 연산자 오버로딩은 반드시 클래스 형에 대해서만 가능하다. • 연산자 함수는 반드시 클래스의 멤버 함수 또는 전역 함수로 구현해야 한다. • 단항 연산자에 대한 연산자 함수를 멤버 함수로 정의하면 인자 없는 함수로 정의된다. 만일 전역 함수로 정의하면 인자가 하나인 함수로 정의된다. • 이항 연산자에 대한 연산자 함수를 멤버 함수로 정의하면 인자가 하나인 함수로 정의된다. 만일 전역 함수로 정의하면 인자가 두 개인 함수로 정의된다. • 연산자 함수는 디폴트 인자를 가질 수 없다. • 대입 연산자를 제외한 모든 연산자 함수를 상속받을 수 있다. • = 연산자, () 연산자, [] 연산자, -> 연산자는 반드시 연산자 함수를 멤버 함수로 정의해야 한다. • . 연산자, .* 연산자, :: 연산자, ?: 연산자, # 연산자, ## 연산자는 오버로딩할 수 없다. 프로그래밍언어및실습 (C++)
클래스 형 변환 • 특정 데이터 형에서 클래스 형으로의 형 변환은 변환 생성자에 의해서 처리된다. • 클래스 형에서 다른 데이터 형으로의 형 변환이 필요할 때는 변환 연산자가 사용된다. 프로그래밍언어및실습 (C++)
변환 연산자 함수 • 변환 연산자 함수는 “operator 데이터 형” 형태의 이름을 갖는 함수이다. • 리턴형이 없으며, 특별히 void라고 지정하지도 않는다. • 항상 인자 없는 함수로 정의된다. 프로그래밍언어및실습 (C++)
변환 연산자 함수의 예 class String { ... public: operator const char*() const; }; String::operator const char*() const { return m_str; } void Test(const char* s) { cout << "문자열 : " << (::strlen(s) == 0 ? "없음" : s) << "\n"; } int main() { String str1; Test(str1); // 변환 연산자 호출 const char* str2 = str1; // 변환 연산자 호출 } 프로그래밍언어및실습 (C++)
C++의 형 변환 연산자 • C의 형 변환 연산자의 문제점 • 형 변환의 의도를 알기가 어렵다. • C++의 형 변환 연산자 • const_cast • static_cast • dynamic_cast • reinterpret_cast 프로그래밍언어및실습 (C++)
const_cast • const 변수에서 const 속성을 제거하기 위한 형 변환에 사용 String s1(“abc”); const String* pStr1 = &s1; pStr1->Set(“def”); // pStr1이 const String* 형이므로 컴파일 에러 String* pStr2 = const_cast<String*>(pStr1); // const 속성을 제거함 pStr2->Set(“def”); // pStr2이 const String* 형이 아니므로 OK 프로그래밍언어및실습 (C++)
static_cast • 컴파일 시 자동 형 변환이 가능한지 검사 • 형 변환 가능한 경우에는 형 변환을 수행하고 만일 형 변환할 수 없는 경우에는 컴파일 에러를 발생 class Shape { ... }; class Rectangle : public Shape { ... }; Rectangle r1; Shape *pShape = static_cast<Shape*>(&r1); // OK; Rectangle *pRect = static_cast<Rectangle*>(pShape); // 컴파일 에러 프로그래밍언어및실습 (C++)
dynamic_cast • 실행 시간에 형 변환 가능하지 검사 • 만일 형 변환 가능하면 형 변환을 수행하고 형 변환할 수 없는 경우에는 0을 리턴하거나 예외를 발생 Shape* pShape = NULL; int choice; cin >> choice; switch(choice) { case 1 : pShape = new Shape; break; case 2 : pShape = new Rectangle; break; } Rectangle *pRect = dynamic_cast<Rectangle*>(pShape); if( pRect != NULL ) pRect->GetWidth(); 프로그래밍언어및실습 (C++)
RTTI (1) • dynamic_cast 연산자를 사용하려면 C++의 RTTI 기능을 사용해야 한다. • RTTI(Run-Time Type Information) • 실행 시간에 객체의 클래스 형에 대한 정보를 구할 수 있는 기능 • typeid 연산자와 type_info 클래스 제공 Rectangle *pRect = new Rectangle; Shape *pShape = pRect; cout << typeid( pShape ).name() << endl;// class Shape * 출력 cout << typeid( *pShape ).name() << endl;// class Rectangle 출력 cout << typeid( pRect ).name() << endl;// class Rectangle * 출력 cout << typeid( *pRect ).name() << endl; // class Rectangle 출력 delete pRect; 프로그래밍언어및실습 (C++)
RTTI (2) • typeid 연산자 • 변수나 객체의 형 정보를 저장하는 type_info 클래스 객체의 레퍼런스를 리턴 • type_info 클래스 • 데이터 형을 비교하는 == 연산자 함수나 != 연산자 함수, 데이터 형 이름을 문자열로 리턴하는 name 함수를 제공 • type_info 클래스는 컴파일러에 의해서 내부적으로 정의되며 type_info 클래스의 객체를 생성하는 유일한 방법은 typeid 연산자를 이용하는 것이다. 프로그래밍언어및실습 (C++)
reinterpret_cast • 강제 형 변환을 수행 unsigned short Hash( void *p ) { unsigned int val = reinterpret_cast<unsigned int>( p ); return ( unsigned short )( val ^ (val >> 16)); } 프로그래밍언어및실습 (C++)
정리 • 클래스의 객체에 대해서 연산자를 사용할 수 있도록 만드는 기능을 연산자 오버로딩이라고 한다. • 연산자 오버로딩은 연산자 함수에 의해서 구현된다. • 연산자 함수는 클래스의 멤버 함수로 정의할 수도 있고, 전역 함수로 정의할 수도 있다. • 클래스 형과 기본형 사이의 형 변환에 변환 생성자와 변환 연산자가 이용된다. • 클래스 형을 다른 데이터 형으로 변환하려면 변환 연산자를 정의한다. • C++은 형 변환의 의미를 명확히 구분해서 사용할 수 있도록 형 변환 연산자인 const_cast, static_cast, dynamic_cast, reinterpret_cast 연사자를 제공한다. 프로그래밍언어및실습 (C++)