[목차]
(1) 워밍업~
(2) 함수 겹지정 & 기본 인수
(3) static 멤버 데이타 & static 멤버 함수
(4) 참조
(5) 연산자 겹지정
0. 워밍업~
1. 2진 연산자 겹지정
2. 1진 연산자 겹지정
3. 객체에서 기본형으로(형변환)
4. 객체에서 객체로(형변환)
5. '=' 겹지정
6. '[]' 겹지정
7. 겹지정 연산자의 정밀 조정
(5) 연산자 겹지정
0. 워밍업~
- 연산자 겹지정에 대해...
1. 리스팅을 더 직관적으로 만들어 프로그램 작성, 판독 및 유지 관리를
더 쉽게 해 준다.
2. 수치 데이터형을 나타내는 클래스에 사용하는 것이 가장 좋다.
//---------------------------------------------------------------------------
// 작성일 : 2001.08.21
// 제 목 : 연산자 겹지정 스트링 더하기 ( + 겹지정 )
// 작성자 : 남병철
//---------------------------------------------------------------------------
#include
#include
#include
#pragma hdrstop
//---------------------------------------------------------------------------
class xString
{
private:
enum { MAX = 80 };
char str[ MAX ];
public:
xString() { strcpy( str, "" ); }
xString( char s[] ) { strcpy( str, s ); }
void input() { cin.get( str, MAX ); }
void display() { cout << str; }
xString operator+( xString right ) {
xString temp;
if( strlen( str ) + strlen( right.str ) < MAX - 1 ) {
strcpy( temp.str, str );
strcat( temp.str, right.str );
} else {
cout << "\nError : xString too long" << endl;
}
return temp;
}
};
//---------------------------------------------------------------------------
#pragma argsused
void main()
{
xString s1( "Greetings, " );
xString s2, s3;
cout << "Enter your name : ";
s2.input();
s3 = s1 + s2 + ".";
s3.display();
getch();
}
//---------------------------------------------------------------------------
(결과)
Enter your name : Nam, Byeong Cheol
Greetings, Nam, Byeong Cheol.
//---------------------------------------------------------------------------
1. 2진 연산자 겹지정
- 관계형 연산자(>, <, >=, <= ... )
리턴값은 참/거짓을 의미하므로 아래와 같이 리턴값에 이름을 부여한다.
Ex) enum boolean { FALSE = 0, TRUE };
//---------------------------------------------------------------------------
// 작성일 : 2001.08.22
// 제 목 : 관계형 연산자
// 작성자 : 남병철
//---------------------------------------------------------------------------
#include
#include
#pragma hdrstop
//---------------------------------------------------------------------------
enum boolean { FALSE, TRUE };
class airtime
{
private:
int hours;
int minutes;
public:
void display() const {
cout << hours << ':' << minutes;
}
void get() {
char dummy;
cout << "\nEnter time (format 12:59) : ";
cin >> hours >> dummy >> minutes;
}
bool operator < ( const airtime& right ) {
if( hours < right.hours )
return TRUE;
if( hours == right.hours && minutes < right.minutes )
return TRUE;
return FALSE;
}
};
//---------------------------------------------------------------------------
#pragma argsused
void main()
{
airtime at1, at2;
cout << "Enter first airtime.";
at1.get();
cout << "Enter second airtime.";
at2.get();
if( at1 < at2 )
cout << "\nfirst less than second";
else
cout << "\nfirst not less than second";
getch();
}
//---------------------------------------------------------------------------
(결과)
Enter first airtime.
Enter time (format 12:59) : 10:04
Enter second airtime.
Enter time (format 12:59) : 10:05
first less than second
//---------------------------------------------------------------------------
- 대입 연산자( +=, -=, *=, /= ... )
보통의 2진 연산자와 대입 연산자의 가장 큰 차이점은 대입 연산자가 호출 객체를
수정한다는 것이다.
Ex) at1 += at2;
대입 연산자는 값을 리턴하는 것도 중요한 목적이다.
(대입 연산자는 리턴값이 사용되든 그렇지 않든, 대개 자체 클래스의 값을 리턴
하여 이런 식의 변경을 가능하게 한다.)
Ex) at3 = at1 += at2;
아래 코드에서 += overload에서 += 함수 내부에 임시 객체를 만들지 않고 바로
리턴을 사용한 것은 메모리 관리상 잘한 코드이다.
Ex) return airtime( hours, minutes );
airtime 객체를 생성해서 리턴하는 장면(?)이다.
컴파일러는 똑똑하므로 airtime객체의 값을 airtime객체에 대입하게된다.
이렇게 함으로써 += 함수 내부에 임시 덧셈 객체 생성을 막을 수 있다.
return 할 경우에 1번의 객체 생성으로 마무리 된다..
( ㅡ.ㅡ; 휴~ 책의 번역 질이 영~~ 형편없군.. ㅡ.ㅡ; 간신히 이해했넴.. )
//---------------------------------------------------------------------------
// 작성일 : 2001.08.26
// 제 목 : 대입 연산자
// 작성자 : 남병철
//---------------------------------------------------------------------------
#include
#include
#pragma hdrstop
//---------------------------------------------------------------------------
class airtime
{
private:
int hours;
int minutes;
public:
airtime() : hours( 0 ), minutes( 0 ) {}
airtime( int h, int m ) : hours( h ), minutes( m ) {}
void display() const {
cout << hours << ':' << minutes;
}
void get() {
char dummy;
cout << "\nEnter time (format 12:59) : ";
cin >> hours >> dummy >> minutes;
}
airtime operator += ( const airtime& right ) {
hours += right.hours;
minutes += right.minutes;
if( minutes >= 60 ) {
hours++;
minutes -= 60;
}
return airtime( hours, minutes );
}
};
//---------------------------------------------------------------------------
#pragma argsused
void main()
{
airtime at1, at2, at3;
cout << "Enter first airtime.";
at1.get();
cout << "Enter second airtime.";
at2.get();
at1 += at2;
cout << "\nat1 += at2 = ";
at1.display();
at3 = at1 += at2;
cout << "\nat3 = ";
at3.display();
getch();
}
//---------------------------------------------------------------------------
(결과)
Enter first airtime.
Enter time (format 12:59) : 5:5
Enter second airtime.
Enter time (format 12:59) : 6:6
at1 += at2 = 11:11
at3 = 17:17
//---------------------------------------------------------------------------
2. 1진 연산자 겹지정
대부분의 1진 연산자는 객체 왼쪽에만 나타날 수 있지만, ++와 -- 연산자는
왼쪽과, 오른쪽 모두 나타날 수 있다.
즉, 접두어 버전은 정상적이지만 접미어 버전은 예외인 것이다.
- 접두어, 접미어 (++)
접두어 버전과 접미어 버전의 차이를 보자.
airtime operator++ (); // PREFIX
airtime operator++ ( int ); // POSTFIX
//---------------------------------------------------------------------------
// 작성일 : 2001.08.26
// 제 목 : 1진 연산자 겹지정(PREFIX, POSTFIX)
// 작성자 : 남병철
//---------------------------------------------------------------------------
#include
#include
#pragma hdrstop
//---------------------------------------------------------------------------
class airtime
{
private:
int hours;
int minutes;
public:
airtime() : hours( 0 ), minutes( 0 ) {}
airtime( int h, int m ) : hours( h ), minutes( m ) {}
void display() const {
cout << hours << ':' << minutes;
}
void get() {
char dummy;
cout << "\nEnter time (format 12:59) : ";
cin >> hours >> dummy >> minutes;
}
airtime operator++ () {
++minutes;
if( minutes >= 60 ) {
++hours;
minutes -= 60;
}
return airtime( hours, minutes );
}
airtime operator++ ( int ) {
airtime temp( hours, minutes );
++minutes;
if( minutes >= 60 ) {
++hours;
minutes -= 60;
}
return temp;
}
};
//---------------------------------------------------------------------------
#pragma argsused
void main()
{
airtime at1, at2;
cout << "\nSet at1";
at1.get();
cout << endl << endl << "PREFIX";
at2 = ++at1;
cout << "\nat2 = ++at1";
cout << "\nat2 = ";
at2.display();
cout << endl << endl << "POSTFIX";
at2 = at1++;
cout << "\nat2 = at1++";
cout << "\nat1 = ";
at1.display();
cout << "\nat2 = ";
at2.display();
getch();
}
//---------------------------------------------------------------------------
(결과)
Set at1
Enter time (format 12:59) : 1:1
PREFIX
at2 = ++at1;
at2 = 1:2
POSTFIX
at2 = at1++;
at1 = 1:3
at2 = 1:2
//---------------------------------------------------------------------------
- 1진 마이너스 연산자
객체에 부호를 정한다.
'--' PREFIX버전과 같은 방식의 선언을 사용하며 작동 내용을 프로그래밍 하면
된다.
Ex) English operator- () {} // 선언
궂이 예제 코드를 들먹이지 않아도 -- 연산자를 상기하면 별 문제없이 프로그래
밍할 수 있을것이다.
3. 객체에서 기본형으로(형변환)
- 기본형에서 기본형으로
기본형에는 알맞은 형변환 루틴이 내장되어있다.
예전 C에서 사용하던 문법이 아닌 새로운 문법을 적용하는 것이 바람직
하겠다.
Ex) var2 = long( var1 ); // '기능적' 문법 : long연산자라 부름
var2 = (long)var1; // 대체 구문 (C에서 사용하던 구식 문법이다.)
- 기본형에서 객체로
일반적으로 char*를 xString객체로 만드는 작업이다.
( 생성자 부분의 "3. 1인수 생성자"를 참고 바람 )
- 객체에서 기본형으로
객체에서 기본형으로 형변환 예제를 직접 살펴보면서 의미를 보자.
* 영국식 거리(feet, inches) --> 미국식 거리(meter)
리턴값으로 float()연산자를 사용했다. 또한 명시적 형변환으로 리스팅을
읽는 사람이 더 확실히 알아볼 수 있다.
//---------------------------------------------------------------------------]
// 작성일 : 2001.08.27
// 제 목 : 영국식 거리 객체를 미국식 거리(미터,float형)로 형변환
// 작성자 : 남병철
//---------------------------------------------------------------------------
#include
#include
#pragma hdrstop
//---------------------------------------------------------------------------
class English
{
private:
int feet;
float inches;
static const float MTF; // Meter to Feet 변환 상수
public:
English() : feet( 0 ), inches( 0.0 ) {}
English( float meters ) {
float fltfeet = MTF * meters;
feet = int( fltfeet );
inches = 12 * ( fltfeet - feet );
}
English( int f, float i ) : feet( f ), inches( i ) {}
void get() {
cout << " Enter feet : "; cin >> feet;
cout << " Enter inches : "; cin >> inches;
}
void display() {
cout << feet << "\' - " << inches << '\"';
}
operator float() {
float fracfeet = inches / 12;
fracfeet += float( feet );
return fracfeet / MTF;
}
};
const float English::MTF = 3.280833;
//---------------------------------------------------------------------------
#pragma argsused
void main()
{
English engman( 1.9 );
cout << "\nengman = ";
engman.display();
float metman;
metman = float( engman );
// metman = static_cast( engman );
cout << "\nmetman = " << metman << "m";
getch();
}
//---------------------------------------------------------------------------
(결과)
engman = 6' - 2.80299"
metman = 1.9m
//---------------------------------------------------------------------------
* xString객체 --> char*
if( strcmp( s1, s2 ) == 0 )
cout << "You're George!";
else
cout << "You aren't George.";
위의 비교 문구에서 객체 s1, s2는 리턴될때 char*로 캐스팅 될것이다.
일반적으로 내장되어있는 형변환에서는 xString을 지원하지 않기 때문에
E2285 Could not find a match for 'strcmp(xString,xString)'
이러한 오류 메시지를 띄울것이다.
그러므로 Class 리턴시를 대비해서 char*()연산자를 overload해 놓으면
xString 리턴시에는 사용자 정의 char*()연산자를 컴파일러는 사용할 것이다.
(위 예제에서는 strcmp는 아래와 같이 char*를 인자로 받아들인다.
int strcmp(const char *s1, const char *s2);
즉, strcmp함수 적용시에는 xString객체라 하더라도 char*로 캐스팅 될것이다.
그러므로 사용자가 직접 정의한 char*()연산자고 호출되어 xString객체의
char* 멤버변수인 str을 리턴한다.(사실 배열이지만 개념상 같다.))
//---------------------------------------------------------------------------
// 작성일 : 2001.08.27
// 제 목 : xString객체를 char* 또는 배열로 변환되어지는 부분을 보여준다.
// 작성자 : 남병철
//---------------------------------------------------------------------------
#include
#include
#pragma hdrstop
//---------------------------------------------------------------------------
class xString
{
private:
enum { MAX = 80 };
char str[ MAX ];
public:
xString() {
strcpy( str, "" );
}
xString( char s[] ) {
strcpy( str, s );
}
void input() {
cin.get( str, MAX );
}
void display() {
cout << str;
}
operator char*() {
return str;
}
};
//---------------------------------------------------------------------------
#pragma argsused
void main()
{
xString s1( "George" );
xString s2;
cout << "Enter your name : ";
s2.input();
if( strcmp( s1, s2 ) == 0 )
cout << "You're George!";
else
cout << "You aren't George.";
getch();
}
//---------------------------------------------------------------------------
(결과)
Enter your name : Geroge
You aren't George.
//---------------------------------------------------------------------------
* 또하나의 형변환 연산자
feet = int( fltfeet );
feet = static_case( fltfeet );
위의 두 가지는 같은 효과를 가지지만 아래가 더 확실하게 이루어지게 한다.
간단한 예를 들면..
키워드 static_cast를 검색하여 실제적 형변환이 이루어지는 부분을 모두 찾는데
용이하게 쓰인다.
위에서 작성한 English 프로그램에서 main의 한 부분을 대체할 수 있다.
metman = float( engman );
metman = static_cast( engman );
같은 코드이지만 훨씬 가독성이 높다.
오류적고 관리하기 쉬운 코드를 만들고자 한다면 권장한다.
4. 객체에서 객체로(형변환)
- 언제 변환하는가?
두 클래스가 모두 같은 사물을 측정하는 수치 수량을 나타낸다면, 클래스간 변환이
적당할 수 있다.
여기서는 간단한 변환예를 들어본 후 Friend를 배운후 좀더 멋진 변환을 다루겠다.
//---------------------------------------------------------------------------]
// 작성일 : 2001.08.27
// 제 목 : 객체 사이의 변환( 1인수 생성자 및 operator객체 )
// 작성자 : 남병철
//---------------------------------------------------------------------------
#include
#include
#pragma hdrstop
//---------------------------------------------------------------------------
class alpha
{
private:
int ia;
public:
alpha( int i ) {
ia = i;
}
void display() {
cout << endl << "ia = " << ia;
};
int get_ia() {
return ia;
}
};
//---------------------------------------------------------------------------
class beta
{
private:
int ib;
public:
beta( alpha a ) {
ib = a.get_ia();
}
void display() {
cout << endl << "ib = " << ib;
};
operator alpha() {
return alpha( ib );
}
};
//---------------------------------------------------------------------------
#pragma argsused
void main()
{
alpha a( 11 );
a.display();
beta b( a );
b.display();
a = static_cast( b );
a.display();
getch();
}
//---------------------------------------------------------------------------
ia = 11
ib = 11
ia = 11
//---------------------------------------------------------------------------
5. '=' 겹지정
- 연쇄가 가능한 대입 연산자
대입 연산자 operator=의 overload는 복사생성자와 일반적으로 같은 일을 하지만
반응 시기가 다르다. 또한 객체의 데이타를 맵핑시킬때 임의적 변환이 가능하므로
사용자 정의에 용의하다.
(2가지 정도의 개선책이 있지만 일단 간단한 연쇄 operator= overload를 보자.)
//---------------------------------------------------------------------------]
// 작성일 : 2001.08.27
// 제 목 : = 대입 연산자 사용( 연쇄 시키기 )
// 작성자 : 남병철
//---------------------------------------------------------------------------
#include
#include
#pragma hdrstop
//---------------------------------------------------------------------------
class omega
{
private:
enum { size = 20 };
char name[ size ];
static int total;
int number;
public:
omega( char str[] ) : number( ++total ) {
strncpy( name, str, size );
cout << "\n1-arg constructor has "
<< "created " << name << "-" << number;
}
~omega() {
cout << "\nDestructor has "
<< "destroyed " << name << "-" << number;
}
omega operator=( const omega& right ) {
cout << "\n\n" << right.name << "-" << right.number
<< " assigned to " << name << "-" << number;
strncpy( name, right.name, size );
cout << ", making " << name << "-" << number;
return omega( name );
}
};
int omega::total = 0;
//---------------------------------------------------------------------------
#pragma argsused
void main()
{
omega om1( "Harriet" );
omega om2( "Bernard" );
omega om3( "Damien" );
om3 = om2 = om1;
getch();
}
//---------------------------------------------------------------------------
(결과)
1-arg constructor has created Harriet-1
1-arg constructor has created Bernard-2
1-arg constructor has created Damien-3
Harriet-1 assigned to Bernard-2, making Harriet-2
1-arg constructor has created Harriet-4
Harriet-4 assigned to Damien-3, making Harriet-3
1-arg constructor has created Harriet-5
Destructor has destroyed Harriet-5
Destructor has destroyed Harriet-4
Destructor has destroyed Harriet-3
Destructor has destroyed Harriet-2
Destructor has destroyed Harriet-1
//---------------------------------------------------------------------------
6. '[]' 겹지정
- 왜? []를 겹지정 하는가?
대개 배열 원소에 접근하는데 사용하는 첨자 연산자 []는 배열의 작동 방법을 수정
하고자 할 때 유용하다.
Ex) 인덱스 경계를 벗어나지 않는 배열
'[]' 겹지정 연산자를 이용하여 인덱스 범위를 넘지않고 자연스러운 첨자 대입이
가능한 객체를 생성해 보자.
객체내부 배열의 특정값을 가져오는 방법으로 아래와 같은 방법이 있다.
temp = sa.access( j ); // 멤버 함수를 이용해 객체 내부 배열 특정값 전달
int& safearay::access( const int& n ) {
if( n < 0 || n >= SIZE ) {
cout << "\nIndex out of bounds"; break;
}
return arr[n]; // 멤번 배열의 인자를 리턴
}
temp = sa.operator[]( j ); // '[]'operator overload 사용
temp = sa[ j ]; // C++에서는 이런 단순화 문법도 사용할 수 있다.
int& operator [] ( int n ) {
if( n < 0 || n >= SIZE ) {
cout << "\nIndex out of bounds"; break;
}
return arr[n];
}
//---------------------------------------------------------------------------]
// 작성일 : 2001.08.27
// 제 목 : [] 연산자의 overload
// 작성자 : 남병철
//---------------------------------------------------------------------------
#include
#include
#pragma hdrstop
//---------------------------------------------------------------------------
class safearay
{
private:
enum { SIZE = 100 };
int arr[ SIZE ];
public:
int& operator [] ( int n );
};
int& safearay::operator [] ( int n )
{
if( n < 0 || n >= SIZE ) {
cout << "\nIndex out of bounds";
exit(1);
}
return arr[n];
}
//---------------------------------------------------------------------------
#pragma argsused
void main()
{
int j;
int temp;
safearay sa;
const int LIMIT = 10;
// 아래의 문구를 보면 자유로이 safearay객체의 대입 및 리턴이 가능하다.
for( j = 0; j < LIMIT; j++ )
sa[j] = j * 10;
for( j = 0; j < LIMIT; j++ ) {
temp = sa[j];
cout << "\nElement " << j << " is " << temp;
}
getch();
}
//---------------------------------------------------------------------------
(결과)
Element 0 is 0
Element 1 is 10
Element 2 is 20
Element 3 is 30
Element 4 is 40
Element 5 is 50
Element 6 is 60
Element 7 is 70
Element 8 is 80
Element 9 is 90
//---------------------------------------------------------------------------
7. 겹지정 연산자의 정밀 조정
- operator overload의 const member function화
일반적으로 호출 객체를 수정하지 않으므로 const이어야 한다.
(+, *, <, ==, 1진 + -(이들의 경우는 오른쪽에 객체가 있다) ... )
그러나 =, +=와 같은 대입 연산자는 그 객체를 수정하며, ++나 -- 1진 연산자도
마찬가지로 const로 만들면 안된다. []의 경우는 호출 객체를 수정할 수 도 있으므로
const이면 안된다.
- operator overload에서 참조로 리턴하는 경우(*this)
연산의 결과가 연산자가 호출된 객체에 놓일 때 바로 그렇다. 대입 연산자와 몇몇
다른 연산자가 그렇다.
Ex) answer = a += b;
a에 대해 operator+=() 함수가 호출되고 a+b 연산의 결과가 a에 대입된다. 따라서
연산 a+=의 리턴값은 a이다. 그이후에 answer에 대입된다.
이경우 참조로 리턴되는데 const가 아닌경우에 더 융통성을 발휘한다. 하지만 상황에
따라서 const일 필요는 없다.(다만 권장할 뿐이다.)
이제까지는 객체를 리턴하기위해 값으로 리턴해 왔다. 그 경우가 참조로 리턴가능할
지라도.
Ex) return airtime( hours, minutes );
하지만 *this를 사용하면 다음과 같이 간략히 그리고 참조로 리턴도 가능하게된다.
Ex) return *this;
//---------------------------------------------------------------------------]
// 작성일 : 2001.08.28
// 제 목 : 겹지정 연산자의 정밀조정(const operator)
// 작성자 : 남병철
//---------------------------------------------------------------------------
#include
#include
#pragma hdrstop
//---------------------------------------------------------------------------
class airtime
{
private:
int hours;
int minutes;
public:
airtime() : hours( 0 ), minutes( 0 ) {}
airtime( int h, int m ) : hours( h ), minutes( m ) {}
void display() const {
cout << hours << ':' << minutes;
}
void get() {
char dummy;
cout << "\nEnter time (format 12:59) : ";
cin >> hours >> dummy >> minutes;
}
airtime operator + ( const airtime& right ) const {
airtime temp;
temp.hours = hours + right.hours;
temp.minutes = minutes + right.minutes;
if( temp.minutes >= 60 ) {
temp.hours++;
temp.minutes -= 60;
}
return temp;
}
};
//---------------------------------------------------------------------------
#pragma argsused
void main()
{
airtime at1, at2;
const airtime noon( 12, 0 );
cout << "Enter first airtime";
at1.get();
at2 = noon + at1; // 가능
// at2 = at1 + noon; // 가능
cout << "sum = ";
at2.display();
getch();
}
//---------------------------------------------------------------------------
(결론)
Enter first airtime
Enter time (format 12:59) : 1:1
sum = 13:1
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// 작성일 : 2001.08.28
// 제 목 : 겹지정 연산자의 정밀조정(*this)
// 작성자 : 남병철
//---------------------------------------------------------------------------
#include
#include
#pragma hdrstop
//---------------------------------------------------------------------------
class airtime
{
private:
int hours;
int minutes;
public:
airtime() : hours( 0 ), minutes( 0 ) {}
airtime( int h, int m ) : hours( h ), minutes( m ) {}
void display() const {
cout << hours << ':' << minutes;
}
void get() {
char dummy;
cout << "\nEnter time (format 12:59) : ";
cin >> hours >> dummy >> minutes;
}
airtime operator += ( const airtime& right ) {
hours += right.hours;
minutes += right.minutes;
if( minutes >= 60 ) {
hours++;
minutes -= 60;
}
return *this;
}
};
//---------------------------------------------------------------------------
#pragma argsused
void main()
{
airtime at1, at2, at3;
const airtime noon( 12, 0 );
cout << "Enter first airtime";
at1.get();
cout << "Enter second airtime";
at2.get();
at1 += at2; // 겹지정된 += 연산자
// at2를 at1에 더함
cout << "\nat1 += at2 = ";
at1.display();
at3 = at1 += at2; // 다시 더하되 리턴값을 사용
cout << "\nat3 = ";
at3.display();
getch();
}
//---------------------------------------------------------------------------
(결론)
Enter first airtime
Enter time (format 12:59) : 1:1
Enter second airtime
Enter time (format 12:59) : 2:2
at1 += at2 = 3:3
at3 = 5:5
//---------------------------------------------------------------------------
구성원 함수로 겹지정된 연산자에 바람직한 함수, 리턴값 및 인수
==================================================================
겹지정 연산자의 유형 리턴 인수 전달 방법 함수
==================================================================
산술(+, -, *, /) 값으로 const 참조 const
대입(=, +=, -=) 참조로(*this) const 참조 비 const
비교(<, ==) 값으로(bool) const 참조 const
1진 접두어(++, --) 참조로(*this) 없음 비 const
1진 접미어(++, --) 값으로 값(모두 int) 비 const
1진(-, +) 값으로 없음 const
참조([]) 참조로 값(정수) 비 const
==================================================================