|7.2| 연산자 중복 (operator overloading)


:  C++의 다형성(polymorphism)이다.  피연산자에 따라 서로 다른 연산을 하도록 동일한 연산자를 중복해서 작성하는 것.



● 연산자 중복의 특징


1) C++에 원래 있는 연산자만 중복 가능

2) 정수/실수 가 아닌 객체나 값

3) 연산자 중복으로 연산자의 우선 순위를 바꿀 수 없음

4) 안되는 연산자도 있음



● 연산자 함수 작성 방법


1) 클래스의 멤버 함수로 구현

2) 외부 함수로 구현하고 클래스의 프랜드 함수로 선언



리턴타입 operator 연산자(parameter);



1) 클래스의 멤버 함수로 선언되는 경우


1
2
3
4
5
6
7
class Color {
    ...
        Color operator + (Color op2); /* 왼쪽 피연산자는 객체 자신이고
                                      오른쪽 피연산자가 op2에 전달*/
    bool operator == (Color op2); /*왼쪽 피연산자는 객체 자신
                                  오른쪽 피연산자가 op2에 전달*/
};
cs



=> + 나 == 의 오른쪽 피연산자만 매개 변수 op2에 전달되고, 왼쪽 피연산자는 객체 자신이므로 매개 변수에 전달되지 않는다.



2) 외부 함수로 구현하고 클래스에 프렌드 함수로 선언하는 경우


1
2
3
4
5
6
7
8
Color operator + (Color op1, Color op2); // 외부 전역 함수
bool operator == (Color op1, Color op2); // 외부 전역 함수
...
class Color {
...
    friend Color operator + (Color op1, Color op2); // 프렌드 선언
    friend bool operator == (Color op1, Color op2); // 프렌드 선언
};
cs




|7.3| 이항 연산자 중복


: 예시를 통해 연산자 중복에 대해 제대로 이해해보자!


1
2
3
4
5
6
7
8
9
10
11
class Power { // 에너지를 표현하는 파워 클래스
    int kick;    // 킥 파워
    int punch;    // 펀치 파워
 
public:
    Power(int kick = 0int punch = 0){
        this->kick = kick;
        this->punch = punch;
    }
 
};
cs


● 1) + 연산자 중복


※ 우선 이 예제에서 +의 의미를 결정해야 하는데, 이 것은 전적으로 개발자의 몫이다.  
   여기서는 +연산을 두 객체의 kick과 punch를 더하는 것으로 정의한다.

1
2
3
Power a(35), b(46), c;
 
= a + b; // 두 객체 a, b의 파워를 더하는 + 연산
cs



※ 컴파일러는 a + b 의 연산이 C++의 기본 더하기로 처리될 수 없음을 판단한다. 따라서 Power 클래스에 Power 객체를 더하는

   + 연산자 함수가 새로 선언되어 있는지 찾는다.

   이를 위해 컴파일러는 a + b를 다음과 같이 변형한다.


1
a . + ( b );
cs


※ 이 식은 다음과 같이 Power 객체 a의 멤버 함수 operator+()를 호출하며, b를 매개 변수로 넘겨주는 함수 호출이다.

우리는 이 호출이 성공할 수 있도록 다음과 같이 operator+()함수를 Power클래스의 멤버 함수로 선언한다.


1
2
3
class Power() {
    Power operator+ (Power op2); // + 연산자 함수 선언
}
cs


※ operator+() 함수는 리턴 타입을 Power로 하고, 더한 결과인 새로운 Power 객체를 리턴한다.


※ 위의 operator+() 함수의 구현은 다음과 같다.


1
2
3
4
5
6
Power Power::operator+(Power op2) {
    Power tmp;
    tmp.kick = this->kick + op2.kick;
    tmp.punch = this - punch + op2.punch;
    return tmp;
}
cs


=> 이 연산자 함수는 더한 결과인 tmp 객체를 리턴한다.



< 예시 구현 >


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
using namespace std;
 
class Power {
    int kick;
    int punch;
public:
    Power(int kick=0int punch=0) {
        this->kick = kick; this->punch = punch;
    }
    void show();
    Power operator+ (Power op2); // + 연산자 함수 선언
};
 
void Power::show() {
    cout << "kick=" << kick << ',' << "punch=" << punch << endl;
}
 
Power Power::operator+(Power op2) { 
    Power tmp; // 임시 객체 생성
    tmp.kick = this->kick + op2.kick; // kick 더하기
    tmp.punch = this->punch + op2.punch; // punch 더하기
    return tmp; // 더한 결과 리턴
}
 
int main() {
    Power a(3,5), b(4,6), c;
    c = a + b; // 파워 객체 + 연산
    a.show();
    b.show();
    c.show();
}
cs



● 2) == 연산자 중복


: a == b 가 true인 경우 각각의 kick과 punch가 같은 경우로 정의한다. 따라서 operator==()의 리턴 타입은 bool이다.



※ 컴파일러는 a==b의 식을 다음과 같이 변형하여 Power 클래스의 멤버로 작성된 operator==()함수를 찾는다.


1
a. == (b)
cs


※ 이 식은 Power 객체 a의 operator==() 연산자 함수를 호출하고 b를 매개 변수로 넘긴다.


< 예시 구현 >


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
using namespace std;
 
class Power {
    int kick;
    int punch;
public:
    Power(int kick=0int punch=0) {
        this->kick = kick; this->punch = punch;
    }
    void show();
    bool operator== (Power op2);  // == 연산자 함수 선언
};
 
void Power::show() {
    cout << "kick=" << kick << ',' << "punch=" << punch << endl;
}
 
bool Power::operator==(Power op2) {
    if(kick==op2.kick && punch==op2.punch) return true;
    else return false;
}
 
int main() {
    Power a(3,5), b(3,5); // 2 개의 동일한 파워 객체 생성
    a.show();
    b.show();
    if(== bcout << "두 파워가 같다." << endl;
    else cout << "두 파워가 같지 않다." << endl;
}
cs



● 3) += 연산자 중복


: 언뜻 보면 아무 값도 리턴할 필요가 없어 void로 하면 될 것 같지만 

  c = a += b; 처럼 사용될 수도 있다.

  (이 문장은 a += b를 먼저 계산하고 결과를 c에 치환하므로, a와 b를 더한 a를 리턴하도록 작성되어야 한다.)


※ 컴파일러는 a+=b;의 식을 다음과 같이 변형한다.


1
a . += ( b ) ;
cs



< 예시 구현 >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
using namespace std;
 
class Power {
    int kick; 
    int punch; 
public:
    Power(int kick=0int punch=0) {
        this->kick = kick; this->punch = punch;
    }
    void show();
    Power& operator+= (Power op2); // += 연산자 함수 선언
};
 
void Power::show() {
    cout << "kick=" << kick << ',' << "punch=" << punch << endl;
}
 
Power& Power::operator+=(Power op2) {
    kick = kick + op2.kick; // kick 더하기
    punch = punch + op2.punch; // punch 더하기
    return *this// 합한 결과 리턴
}
 
int main() {
    Power a(3,5), b(4,6), c;
    a.show();
    b.show();
    c = a += b; // 파워 객체 더하기
    a.show();
    c.show();
}
cs



※ this를 이용하여 return한다는 점에 주목하자.



● 4) + 연산자 b = a + 2 ;


※ 컴파일러는 우선 a + 2 를 다음과 같이 변형할 것이다.


1
a . + ( 2 )
cs


위의 + 연산자 중복 예제에서 파라미터를 int op2로 바꿔주면 되겠다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
using namespace std;
 
class Power {
    int kick; 
    int punch; 
public:
    Power(int kick=0int punch=0) {
        this->kick = kick; this->punch = punch;
    }
    void show();
    Power operator+ (int op2); // + 연산자 함수 선언
};
 
void Power::show() {
    cout << "kick=" << kick << ',' << "punch=" << punch << endl;
}
 
Power Power::operator+(int op2) {
    Power tmp; // 임시 객체 생성
    tmp.kick = kick + op2; // kick에 op2 더하기
    tmp.punch = punch + op2; // punch에 op2 더하기
    return tmp; // 임시 객체 리턴
}
 
int main() {
    Power a(3,5), b;
    a.show();
    b.show();
    b = a + 2// 파워 객체와 정수 더하기
    a.show();
    b.show();
}
cs




|7.4| 단항 연산자 중복


: 피연산자가 하나인 단항 연산자의 연산자 함수

1) 전위 연산자 : !op, ~op, ++op, --op
2) 후위 연산자 ; op++, op--

● 전위 ++ 연산자 중복

1
2
Power a(3,5), b;
= ++a;
cs


: 객체 a의 멤버들의 값을 각각 1씩 증가시킨 후 변경된 객체 a를 리턴시킨다.


※ 컴파일러는 ++a 식을 다음과 같이 변형한다


1
a . ++ ( );
cs



< 예시 구현 >


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
using namespace std;
 
class Power {
    int kick;
    int punch; 
public:
    Power(int kick=0int punch=0) {
        this->kick = kick; this->punch = punch;
    }
    void show();
    Power operator++ (); // 전위 ++ 연산자 함수 선언
};
 
void Power::show() {
    cout << "kick=" << kick << ',' << "punch=" << punch << endl;
}
 
Power Power::operator++() {
    kick++;
    punch++;
    return *this; // 변경된 객체 자신(객체 a)의 참조 리턴
}
 
int main() {
    Power a(3,5), b;
    a.show();
    b.show();
    = ++a; //  전위 ++ 연산자 사용
    a.show();
    b.show();
}
cs



< 예시 : ! 연산자 작성 >


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
using namespace std;
 
class Power {
    int kick;
    int punch; 
public:
    Power(int kick=0int punch=0) {
        this->kick = kick; this->punch = punch;
    }
    void show();
    bool operator! (); // ! 연산자 함수 선언
};
 
void Power::show() {
    cout << "kick=" << kick << ',' << "punch=" << punch << endl;
}
 
bool Power::operator!() {
    if(kick == 0 && punch == 0return true;
    else return false;
}
 
int main() {
    Power a(0,0), b(5,5);
    if(!acout << "a의 파워가 0이다."  << endl// ! 연산자 호출
    else cout << "a의 파워가 0이 아니다." << endl;
    if(!bcout << "b의 파워가 0이다." << endl// ! 연산자 호출
    else cout << "b의 파워가 0이 아니다." << endl;
}
cs



● 후위 ++ 연산자 중복


: a++는 증가 이전의 객체 a를 리턴하는 것으로 정의한다.


※ a++ vs ++a


1
2
Power operator++();        // ++a
Power operator++(int x);    // a++
cs


※ 후위 연산자 함수에서 x에는 의미 없는 값이 전달되므로 무시해도 된다.


※ 컴파일러는 a++식을 다음과 같이 변형한다.


1
a .++(임의의 정수); // 임의의 정수가 operator++(int)의 매개 변수로 넘겨진다.
cs


< 예시 구현 >


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
using namespace std;
 
class Power {
    int kick; 
    int punch; 
public:
    Power(int kick=0int punch=0) {
        this->kick = kick; this->punch = punch;
    }
    void show();
    Power operator++ (int x); // 후위 ++ 연산자 함수 선언
};
 
void Power::show() {
    cout << "kick=" << kick << ',' << "punch=" << punch << endl;
}
 
Power Power::operator++(int x) {
    Power tmp = *this; // 증가 이전 객체 상태를 저장
    kick++;
    punch++;
    return tmp// 증가 이전 객체 상태 리턴
}
 
int main() {
    Power a(3,5), b;
    a.show();
    b.show();
    b = a++// 후위 ++ 연산자 사용
    a.show(); // a의 파워는 1 증가됨
    b.show(); // b는 a가 증가되기 이전 상태를 가짐
}
cs



● (다시) 전위 연산자 vs 후위 연산자 함수 비교



전위 연산자


1
2
3
4
5
Power Power::operator++() {
    kick++;
    punch++;
    return *this// 변경된 객체 자신 리턴
}
cs



후위 연산자


1
2
3
4
5
6
Power Power::operator++(int x) {
    Power tmp = *this// 증가 이전의 객체 상태 저장
    kick++;
    punch++;
    return tmp; // 증가 이전의 객체 리턴
}
cs





|7.5| 프렌드를 이용한 연산자 중복


: 지금까지는 ' 1) 클래스의 멤버 함수로 구현 의 방법' 으로 연산자 함수를 작성하였다.

이번에는 ' 2) 외부 함수로 구현하고 클래스의 프랜드 함수로 선언 ' 으로 작성한다 (외부 전역 함수로 작성)



● 2 + a를 위한 + 연산자 함수 작성


1
2
Power a(34), b;
= 2 + a;
cs


위의 내용에 따르면, 컴파일러는 2 + a 의 연산을 다음과 같이 변형하여 operator+() 함수를 호출하려고 할 것이다.


1
2 . + ( a )
cs


근데 2가 객체가 아니네??


-> 컴파일러는 다음과 같은 식으로 변환한다. 


1
+ ( 2 , a )
cs



=> 사실 컴파일러에게 두 개의 옵션이 있는 셈이다.


위의 식이 성공적이기 위해서는 operator+()함수를 Power클래스의 외부 함수로 밖에 구현할 수 없다.


1
2
3
4
5
6
Power operator+ (int op1, Power op2) {
    Power tmp;
    tmp.kick = op1 + op2.kick;
    tmp.punch = op1 + op2.punch;
    return tmp;
}
cs



● 외부 연산자 함수의 프렌드 선언



: 근데 문제가 있음. 함수 내에서 Power의 private 멤버인 kick과 punch를 자유롭게 접근하기 떄문에 연산자 함수에 컴파일 오류가 생길 것이다.


그럼, kick과 punch를 public으로 선언하면 어떨까?


-> 근데 그럼 연산자 함수를 쓰려고 Power 클래스의 캡슐화의 원칙을 무너뜨리게 되는 치명적인 판단 미스가 된다!



===> friend를 사용하면 깔끔하게 해결된다. 외부에 구현된 operator+(int, Power)의 연산자 함수를 Power 클래스에 프렌드로 초대한다.



1
2
3
4
5
6
7
8
9
10
11
class Power {
    int kick;
    int punch;
public:
    ...
        friend Power operator+(int op1, Power op2); // 프렌드 선언
};
 
Power operator+(int op1, Power op2) { // 외부 함수로 구현된 연산자 함수
    ...
}
cs



< 예시 구현 >


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
using namespace std;
 
class Power {
    int kick; 
    int punch; 
public:
    Power(int kick=0int punch=0) {
        this->kick = kick; this->punch = punch;
    }
    void show();
    friend Power operator+(int op1, Power op2); // 프렌드 선언
};
 
void Power::show() {
    cout << "kick=" << kick << ',' << "punch=" << punch << endl;
}
 
Power operator+(int op1, Power op2) {
    Power tmp; // 임시 객체 생성
    tmp.kick = op1 + op2.kick; // kick 더하기
    tmp.punch = op1 + op2.punch; // punch 더하기
    return tmp; // 임시 객체 리턴
}
 
int main() {
    Power a(3,5), b;
    a.show();
    b.show();
    b = 2 + a; // 파워 객체 더하기 연산
    a.show();
    b.show();
}
cs




● a + b 를 외부 프렌드 함수로 작성


1
2
3
4
5
6
Power operator+(Power op1, Power op2) {
    Power tmp;
    tmp.kick. = op1.kick + op2.kick;
    tmp.punch = op1.punch + op2.punch;
    return tmp;
}
cs


를 외부 전역 함수로 구현하고, Power 클래스 내에


1
friend Power operator+(Power op1, Power op2); 
cs


를 구현하면 되겠다.


※ 동일한 연산자 함수를 멤버 함수와 프렌드 함수로 동시에 구현할 수 없다.




● 단항 연산자 ++를 프렌드로 작성하기



전위 연산자


1
2
3
4
5
Power operator++(Power& op) {
    op.kick++;
    op.punch++;
    return op;
}

cs



후위 연산자


1
2
3
4
5
6
Power operator++(Power& op, int x) {
    Power tmp = op;
    op.kick++;
    op.punch++;
    return tmp;
}
cs



클래스 내에


1
2
friend Power operator++(Power& op);
friend Power operator++(Power& op, int x);
cs


※ 참조 매개 변수를 사용하지 않으면 복사본이 전달되므로 op 객체의 kick과 punch의 값이 변하지 않는다.

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

#8-2 상속과 객체 포인터  (0) 2018.10.07
#8-1 상속  (0) 2018.10.07
#07-A 프렌드  (0) 2018.10.05
#6 함수 중복과 static 멤버  (0) 2018.10.05
#5-B 복사 생성자  (0) 2018.10.05

7.1 C++의 프렌드 개념

※ 프렌드 함수를 선언할 수 있는 경우


1) 클래스 외부에 작성된 함수를 프렌드로 선언
2) 다른 클래스의 멤버 함수를 프렌드로 선언
3) 다른 클래스의 모든 멤버 함수를 한번에 프렌드로 선언



1) 클래스 외부에 작성된 함수를 프렌드로 선언


※ 예제 1)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;
 
class Rect; // forward decalaration. 이 라인이 없으면, 다음 라인에서 Rect를 참조하는 전방 참조(forward reference) 문제 발생
bool equals(Rect r, Rect s); // equals() 함수 선언
 
class Rect { // Rect 클래스 선언
    int width, height;
public:
    Rect(int width, int height)  {     this->width = width; this->height = height;    }
    friend bool equals(Rect r, Rect s); //프렌드 함수 선언
};
 
bool equals(Rect r, Rect s) { // 외부 함수
    if(r.width == s.width && r.height == s.height) return true
    else return false;
}
 
int main() {
    Rect a(3,4), b(4,5);
    if(equals(a, b)cout << "equal" << endl;
    else cout << "not equal" << endl;
}
cs



※ 전방 참조(forward reference)를 해결하기 위해 전방 선언(forward declaration)를 해주었다.



2) 다른 클래스의 멤버 함수를 프렌드로 선언


※ 예제 2)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
using namespace std;
 
class Rect; 
 
class RectManager { // RectManager 클래스 선언
public:
    bool equals(Rect r, Rect s);
};
 
class Rect { // Rect 클래스 선언
    int width, height;
public:
    Rect(int width, int height) { this->width = width; this->height = height; }
    friend bool RectManager::equals(Rect r, Rect s); // 프렌드 함수 선언
};
 
bool RectManager::equals(Rect r, Rect s) { // RectManager::equals() 구현
    if(r.width == s.width && r.height == s.height) return true
    else return false;
}
 
int main() {
    Rect a(3,4), b(3,4);
    RectManager man;
    
    if(man.equals(a, b)) cout << "equal" << endl;
    else cout << "not equal" << endl;
}
cs



3) 다른 클래스의 모든 멤버 함수를 한번에 프렌드로 선언



※ 예제 3)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
using namespace std;
 
class Rect;
 
class RectManager { // RectManager 클래스 선언
public:
    bool equals(Rect r, Rect s);
    void copy(Rect& dest, Rect& src);
};
 
class Rect { // Rect 클래스 선언
    int width, height;
public:
    Rect(int width, int height)  { this->width = width; this->height = height; }
    friend RectManager; // RectManager 클래스의 모든 함수를 프렌드 함수로 선언
};
 
bool RectManager::equals(Rect r, Rect s) { // r과 s가 같으면 true 리턴
    if(r.width == s.width && r.height == s.height) return true
    else return false;
}
 
void RectManager::copy(Rect& dest, Rect& src) { // src를 dest에 복사
    dest.width = src.width;  dest.height = src.height;
}
 
int main() {
    Rect a(3,4), b(5,6);
    RectManager man;
    
    man.copy(b, a); // a를 b에 복사한다.
    if(man.equals(a, b)) cout << "equal" << endl;
    else cout << "not equal" << endl;
}
cs



※ 프렌드 선언은 클래스 내에 private, public 등 아무 위치에서나 가능하다.

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

#8-1 상속  (0) 2018.10.07
#7-B 연산자 중복  (0) 2018.10.07
#6 함수 중복과 static 멤버  (0) 2018.10.05
#5-B 복사 생성자  (0) 2018.10.05
#5-A 함수와 참조  (0) 2018.10.04

|6.1| 함수 중복



※ 중복 함수 조건  


1) 함수 이름 동일

2) 매개 변수 타입 or 개수 달라야 함


but!  return 타입은 고려되지 않음



※ 이미 생성자 함수 중복으로 함수 중복을 해본 바 있음


※ string 클래스 또한 다양한 생성자를 제공함


1
2
3
4
5
string str; // 빈 문자열을 가진 객체
string address("Hermine Portejoie");
string copyname(frenchkebab); /* string객체인 frenchkebab의 문자열을 복사한
                              별도의 copyname객체 생성*/
 
cs



|6.2| 디폴트 매개 변수


※ 조건


1) 디폴트 매개 변수에 값을 전달하는 것은 선택 사항이지만, 일반 매개 변수는 반드시 값을 전달해야 한다.


2) 디폴트 매개 변수는 오른쪽 끝에 몰려 선언되어야 한다.


1
2
void func(int a, int b = 5int c); // 컴파일 오류
void funct(int a = 0int b); // 컴파일 오류
cs



※ 예시 : 디폴트 매개 변수의 원형 선언과 구현을 분리


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <string>
using namespace std;
 
// 원형 선언
void star(int a=5);
void msg(int id, string text="");
 
// 함수 구현
void star(int a) {
    for(int i=0; i<a; i++cout << '*';
    cout << endl;
}
 
void msg(int id, string text) {
    cout << id << ' ' << text << endl;
}
 
int main() {
    star(); // star(5);와 동일
    star(10);
 
    msg(10); // star(10, "");과 동일
    msg(10"Hello");
}
 
cs


※ 디폴트 매개 변수의 장점 : 함수 중복을 간소화 할 수 있다.



1
2
3
4
5
6
7
class Circle {
    .........
public:
    Circle() { radius = 1; }
    Circle(int r) { radius = r; }
    ............
};
cs

얘를


1
2
3
4
5
class Circle {
    .........
public:
    Circle(int r = 1) { radius = r; }
};
cs

이렇게 간소화 할 수 있다.




|6.3| 함수 중복의 모호성


※ 함수 중복으로 인한 모호성 3가지

1) 형 변환으로 인해
2) 참조 매개 변수로 인해
3) 디폴트 매개 변수로 인해



1) 형 변환으로 인한 모호성


1
2
3
double square(double a);
....
square(3); // double 타입 매개 변수에 int 타입 전달 but 오류 X
cs


※ 형 변환 참조 => '여기'


but!


1
2
float square(float a);
double square(double a);
cs


이렇게 2개의 중복된 함수가 있을 경우


1
square(3); // float? double? => 컴파일 오류 
cs


이렇게 컴파일러가 오류를 발생시킨다.



※ 예제


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
using namespace std;
 
float square(float a) {
    return a*a;
}
 
double square(double a) {
    return a*a;
}
 
int main() {
    cout << square(3.0); // square(double a); 호출
    cout << square(3); // 중복된 함수에 대한 모호한 호출로서, 컴파일 오류
}
cs


※ 성돈tip : 실수 default가 double이므로, 3.0f 로 입력해야 float값으로 들어간다.



2) 참조 매개 변수로 인한 모호성



1
2
3
4
5
int add(int a, int b);
int add(int a, int &b);
 
int s = 10, t = 20;
add(s, t); // 컴파일 오류
cs



3) 디폴트 매개 변수로 인한 모호성


1
2
3
4
void msg(int id)
void msg(int id, string s = "")
 
msg(6); // 컴파일 
cs








|6.4| static 멤버


● static의 특성


- 생명주기 : 프로그램이 시작할 때 생성, 종료할 때 소멸

- 사용 범위 : global or local




※ non-static 멤버와 구별
: non-static 멤버는 객체가 생성될 때 각 객체마다 개별적으로 생성된다. ( 객체와 생명 주기를 같이 한다. )
 하지만 static 멤버는 객체가 생기기 이전에 이미 생성되어 있고, 객체가 사라져도 소멸되지 않고, 모든 객체들의 공통된 멤버로서 공유된다.
 -> static 멤버는 클래스 당 하나만 생기고 모든 객체들이 공유한다.(class 멤버 라고도 부른다. )

● static 멤버 선언


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person {
public:
    double money; // 개인 소유의 돈
    void addMoney(int money) {
        this->money += money;
    }
 
    static int sharedMoney; // 공금
    static void addShared(int n) {
        sharedMoney += n;
    }
 
};
 
int Person::sharedMoney = 10; // 반드시 전역 공간에 선언한다
cs


※ static 멤버 변수는 외부에 전역 변수로 선언되어야 한다.



● static 멤버 사용 : 객체의 멤버로 접근하는 방법


:  객체 이름 or 객체 포인터 사용


1
2
3
4
5
6
Person lee;
lee.sharedMoney = 500// 객체 이름으로 접근
 
Person *p;
= &lee;
p->addShared(200); // 객체 포인터로 접근
cs




● static 멤버 사용 : 클래스명과 범위지정 연산자(::)로 접근하는 방법


1
2
Person::sharedMoney = 200// 클래스명으로 접근
Person::addShared(200);
cs


참고로,


1
2
3
4
5
6
7
8
Person::sharedMoney = 200;
hermine.sharedMoney = 200// 위와 동일한 표현
 
 
Person::addShared(200);
junghyun.addShared(200); // 위와 동일한 표현
cs



※ 당연하지만 non-static 멤버는 클래스명으로 접근할 수 없다.


Person 객체가 하나도 안생겼을 때에도 static 멤버를 접근하여 사용할 수 있다.




● static의 활용



1) 전역 변수나 전역 함수를 클래스에 캡슐화


1
2
int abs(int a) { return a > 0 ? a : -1; }
int max(int a, int b) { return (a > b) ? a : b; } 
cs


=> 클래스로 캡슐화되어 있지 않고 전역 함수들이 존재하는 좋지 않은 코드 사례이다.



2) 객체 사이에 공유 변수를 만들고자 할 때 


: 당연함. 



※ static 멤버 함수는 오직 static 멤버들만 접근이 가능하다

-> 당연한 것이, 아직 생기지도 안은 객체의 non-static 멤버에 접근한다는 것이 말이 안된다.


※ 하지만 non-static 멤버 함수는 static 멤버를 접근하는데 전혀 문제가 되지 않는다

-> 이것도 당연한 것이 static멤버는 이미 생성되어 있기 때문이다.

his를 사용할 수 없다.

※ static 멤버 함수는 t

-> static 멤버 함수는 객체가 생기기 전부터 호출 가능하므로 this를 사용할 수 없다.

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

#8-1 상속  (0) 2018.10.07
#7-B 연산자 중복  (0) 2018.10.07
#07-A 프렌드  (0) 2018.10.05
#5-B 복사 생성자  (0) 2018.10.05
#5-A 함수와 참조  (0) 2018.10.04