C++ 연산자 오버로딩
C++ 의 연산자 오버로딩에 대해 정리한다.
개요
. 객체도 기본 자료형 변수처럼, 덧셈, 뺄셈 등 연산들을 가능하게 하는 방법
1
2
3
4
...
int a = 10 + 20; // 일반산술연산
Point p3 = p1 + p2; // 객체의 경우, 연산자 오버로딩 함수 호출
...
. operator
키워드와 연산자를 묶어 함수의 이름을 정의
주의할 점
잘못 사용하거나 남용할 경우, 프로그램을 복잡하고 이해하기 어렵게 만들 수 있음
연산자의 우선 순위, 결합성을 바꿀 수 없음
디폴트 매개 변수 설정 불가능
디폴트 연산자들의 기본 기능을 빼앗을 수 없음 ex) + → A + B + C (이항연산을 삼항연산으로 변경 불가능)
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
36
37
38
39
#include <iostream>
using std::endl;
using std::cout;
class Point{
private:
int x, y;
public:
// `:` 멤버 이니셜라이져(member initializer) 활용한 생성자 초기화
Point(int _x=0, int _y=0):x(_x), y(_y){}
void ShowPosition();
void operator+(int val);
};
void Point::ShowPosition(){
cout << x << " " << y << endl;
}
// 연산자 오버로딩
void Point::operator+(int val)
{
x += val;
y += val;
}
int main(void)
{
Point p(1,5);
p.ShowPosition();
p.operator+(10);
// p+10; 위 행을 본 행으로 교체하여도 문제 없이 작동함
// 이 때, p가 기본 자료형 변수라면 단순 덧셈 연산
// p가 객체라면, p.operator+(10) 으로 변경하여 수행
p.ShowPosition();
return 0;
}
. C++ 연산자 오버로딩 개념(위 예제에서)
멤버 함수에 의한 오버로딩
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
#include <iostream>
using std::endl;
using std::cout;
class Point{
private:
int x, y;
public:
Point(int _x=0, int _y=0): x(_x), y(_y){}
void ShowPosition();
Point operator+(const Point& p) const;
};
void Point::ShowPosition(){
cout << x << " " << y << endl;
}
Point Point::operator+(const Point& p) const{
Point temp(x + p.x, y + p.y);
return temp;
}
int main(void)
{
Point p1(1, 2);
Point p2(3, 4);
Point p3 = p1 + p2;
p3.ShowPosition();
return 0;
}
Point Point::operator+(const Point& p) const
이해
. Point 객체를 인자로 전달 받음(Point p)
. 함수 성능 향상을 위해 레퍼런스로 전달받음(Point &)
. 전달 인자의 변경을 허용하지 않음 (const Point& p)
. 함수 내에서 멤 버 변수의 조작을 할 필요 없으므로 함수 상수화(마지막 const)
예제 객체 형성 및 연산 수행과정
. 객체 생성
. 연산 수행
전역 함수에 의한 오버로딩
. 일반적으로 전역 함수에서 객체 멤버로의 접근(private 멤버로의 외부 함수에서 접근)은 불가능
. 따라서, 전역 함수에서 연산자 오버로딩을 통한 객체 멤버의 조작을 위해서 friend
키워드를 활용함
. 전역 함수에 의한 오버로딩은 최소화 하는 것이 바람직
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
36
#include <iostream>
using std::endl;
using std::cout;
class Point{
private:
int x;
int y;
public:
Point(int _x=0, int _y=0):x(_x), y(_y){}
void ShowData();
// 외부에서 객체 멤버로의 접근을 위한 fried 선언
friend Point operator+(const Point& p1, const Point& p2);
};
void Point::ShowData(){
cout << x << " " << y << endl;
}
// 전역함수에 의한 연산자 오버로딩
Point operator+(const Point& p1, const Point& p2)
{
Point temp(p1.x + p2.x, p1.y + p2.y);
return temp;
}
int main(void)
{
Point p1(10, 20);
Point p2(20, 10);
Point p3 = p1 + p2;
p3.ShowData(); // Point p3 = operator+(p1, p2);
return 0;
}
단항 연산자의 오버로딩
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
using std::endl;
using std::cout;
class Point{
private:
int x;
int y;
public:
Point(int _x=0, int _y=0):x(_x), y(_y){}
void ShowData();
Point& operator++();
// 외부에서 객체 멤버로의 접근을 위한 fried 선언
friend Point& operator--(Point& p);
};
void Point::ShowData(){
cout << x << " " << y << endl;
}
Point& Point::operator++() // 멤버 변수에 의한 오버로딩
{
x++;
y++;
return *this; // Class Point로 인해 만들어진 객체 자신을 리턴
}
/*
이미 증가연산을 수행하였는데, 객체 자신을 리턴하는 이유
만약 자신을 리턴하지 않는다면,
++(++p);
(++p).ShowData();
와 같은 연산과 연결되어 수행하는 명령을 실행할 수 없어 컴파일 에러가 발생함
*/
// 전역함수에 의한 연산자 오버로딩
Point& operator--(Point& p)
{
p.x--;
p.y--;
return p;
}
int main(void)
{
Point p(10, 20);
++p; // 객체 p의 x, y 값을 1 씩 증가
p.ShowData();
--p; // 객체 p의 x, y 값을 1 씩 감소
p.ShowData();
++(++p); // 객체 p의 x, y 값을 2 씩 증가
p.ShowData();
--(--p); // 객체 p의 x, y 값을 2 씩 감소
p.ShowData();
return 0;
}
. 위 예제에서 연산자 오버로딩 시, 참조형Point&
를 리턴하는 이유
선 연산과 후연산
. 전위(Prefix), 후위(Postfix) 연산자 선언에 대한 정의
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
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
using std::endl;
using std::cout;
class Point{
private:
int x;
int y;
public:
Point(int _x=0, int _y=0):x(_x), y(_y){}
void ShowData();
Point& operator++();
Point operator++(int);
};
void Point::ShowData(){
cout << x << " " << y << endl;
}
Point& Point::operator++() // Prefix 연산자
{
x++;
y++;
return *this;
}
Point Point::operator++(int) // Postfix 연산자
{
Point temp(x,y); // Point temp(*this);
// 아래 두줄은 ++(*this); 로 표현이 가능함
x++;
y++;
return temp;
}
int main(void)
{
Point p1(10, 20);
(p1++).ShowData(); // 10, 20 출력
p1.ShowData(); // 11, 21 출력
Point p2(10, 20);
(++p2).ShowData(); // 11, 21 출력
return 0;
}
. 후위(Postfix) 연산자 정의의 경우, 연산 전의 상태를 저장하기 위한 Temp 객체를 생성하고, 이를 반환한다. 이 때, Point& 형이 아닌 Point 형으로 반환하는 이유는, 지역객체이기 때문에 함수 호출이 끝나면 객체는 소멸되기 때문임
연산자 오버로딩 관련 교환법칙
피연산자의 값을 변경시키지 않는 연산자 오버로딩
. 아래 연산 방식은, 피연산자의 멤버 변수의 값을 변경 시킴
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
void Point::operator+(int val)
{
x += val;
y += val;
}
int main(void)
{
Point p(1,5);
p.ShowPosition();
p.operator+(10);
// p+10;
p.ShowPosition();
return 0;
}
...
. 피연산자의 멤버 변수의 값을 변경 시키지 않고, 결과를 도출하는 방법
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
36
37
38
39
#include <iostream>
using std::endl;
using std::cout;
class Point{
private:
int x, y;
public:
Point(int _x=0, int _y=0):x(_x), y(_y){}
void ShowPosition();
Point operator+(int val);
};
void Point::ShowPosition(){
cout << x << " " << y << endl;
}
// 임시 Point 객체를 생성하여, 연산을 수행함
// 해당 방식으로, 피연산자의 값 변화 없이 결과를 도출할 수 있음
Point Point::operator+(int val)
{
Point temp(x+val, y+val);
return temp;
}
int main(void)
{
Point p1(1,5);
p1.ShowPosition();
Point p2 = p1.operator+(10);
// p2 = p1 + 10;
cout << "Point p2 member: " <<endl;
p2.ShowPosition();
cout << "Point p1 member: " <<endl;
p1.ShowPosition();
return 0;
}
피연산자의 교환이 가능한 연산자 오버로딩
. 앞의 예제는 피연산자간의 교환이 성립되지 않음
. 이를 해결하기 위해서는 전역함수
에 의한 연산자 오버로딩 방법을 사용해야 함
1
2
3
4
5
6
...
p2 = p1 + 10;
p2 = 10 + p1;
// p2 = 10.operator+(p);
// 위의 형태는 컴파일러가 해석할 수 없음
...
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
36
37
38
39
40
41
42
43
44
45
#include <iostream>
using std::endl;
using std::cout;
class Point{
private:
int x, y;
public:
Point(int _x=0, int _y=0):x(_x), y(_y){}
void ShowPosition();
Point operator+(int val);
// 피연산자의 위치 교환 시, 사용될 함수 선언
// 전역함수 연산자 오버로딩 방법을 사용함
friend Point operator+(int val, Point & p);
};
void Point::ShowPosition(){
cout << x << " " << y << endl;
}
Point Point::operator+(int val)
{
Point temp(x+val, y+val);
return temp;
}
Point operator+(int val, Point & p)
{
return p+val;
}
int main(void)
{
Point p1(1,5);
Point p2 = p1 + 10;
p2.ShowPosition();
// 객체의 피연산 위치가 바뀜, 해당 연산은 전역함수 형태의 연산자 오버로딩 함수를 호출한다
Point p3 = 100 + p2;
p3.ShowPosition();
return 0;
}
임시객체 생성의 의미
. 위 예제의 temp(x+val, y+val)
의 형태를 return Point(x+val, y+val)
로 변경 가능함
1
2
3
4
5
6
Point Point::operator+(int val)
{
return Point(x+val, y+val);
// Point 객체의 이름이 없음
// 해당 줄을 벗어나면 바로 소멸됨
}
. 임시 객체는 "이름이 없으며, 생성한 이후 그 줄에서 사용하지 않으면 바로 소멸됨"
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
#include <iostream>
#include <string.h>
using std::cout;
using std::endl;
class Temp{
char name[30];
public:
Temp(char * _name){
strcpy(name, _name);
cout << name << "객체가 생성됨" << endl;
}
~Temp(){
cout << name << "객체가 소멸됨" << endl;
}
};
int main(void)
{
Temp aaa("aaa object");
cout << "======= 임시 객체 생성 전 =======" << endl;
// 이름이 없는 형태의 임시 객체 생성
Temp("temp object");
cout << "======= 임시 객체 생성 후 =======" << endl;
return 0;
}
연산자 오버로딩을 활용한 C++ 기초 입출력
. cout, cin, endl은 실제로 연산자 오버로딩으로 구현됨
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
36
37
38
#include <iostream>
namespace mystd // mystd 이름 공간
{
char * endl = "\n";
class ostream // 클래스 ostream 정의
{
public:
// 전달 인자에 따른(데이터 타입에 따른) 출력형식 지정
void operator<<(char * str)
{
printf( "%s", str);
}
void operator<<(int i)
{
printf( "%d", i);
}
void operator<<(double i)
{
printf( "%e", i);
}
};
ostream cout; // ostream 객체 생성
}
using mystd::cout;
using mystd::endl;
int main()
{
cout << "Hello \n ";
cout << 3.14;
cout << endl;
cout << 1;
cout << endl;
return 0;
}
. 위의 예제에서,
1
cout << "string" 은 cout 객체의 오버로딩 된 멤버 함수 operator << ("string")의 호출 문장
연산 결합 지원
cout « “Hello” « 100 « endl;
. 연산자를 오버로딩하고 있는 operator « 함수가 cout 객체를 반환해야 함
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
36
37
#include <stdio.h>
namespace mystd // mystd 이름 공간
{
char * endl = "\n";
class ostream // 클래스 ostream 정의
{
public:
// 전달 인자에 따른(데이터 타입에 따른) 출력형식 지정
ostream& operator<<(char * str) // & 참조에 의한 리턴
{
printf( "%s", str);
return *this;
}
ostream& operator<<(int i)
{
printf( "%d", i);
return *this;
}
ostream& operator<<(double i)
{
printf( "%e", i);
return *this;
}
};
ostream cout; // ostream 객체 생성
}
using mystd::cout;
using mystd::endl;
int main()
{
cout << "Hello World" << endl << 3.14 << endl;
return 0;
}
객체 멤버 변수를 출력하는 방법
. 예를 들어 x, y의 두 멤버 변수를 지닌 객체에 대해 출력을 수행할 때,
Point p(1, 2)
cout« p;
. 위의 경우, [ 1, 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
30
31
#include <iostream>
using std::endl;
using std::cout;
// <<의 전역 함수에 의한 오버로딩을 위해, cout 객체를 인자로 전달 받음
// cout 객체는 std의 ostream 클래스에 선언되어 있으므로, ostream을 사용하기 위한 선언이 필요
using std::ostream;
class Point{
private:
int x, y;
public:
Point(int _x=0, int _y=0):x(_x), y(_y){}
friend ostream& operator<<(ostream& os, const Point& p);
// 전역함수가 Point 클래스에 접근하기 위한 friend 선언
};
ostream& operator<<(ostream& os, const Point& p) // 전역 함수에 의한 오버로딩 방식
{
os << " [ " << p.x << " , " << p.y << " ] " << endl;
return os;
}
int main(void)
{
Point p(1, 5);
cout << p; // operator<<(cout, p) 로 호출
return 0;
}
배열의 인덱스 연산자 오버로딩
일반 자료형 배열 인덱스 연산자 오버로딩
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>
using std::endl;
using std::cout;
const int SIZE=3;
class Arr{
private:
int arr[SIZE];
int index;
public: // i = index
Arr():index(0){}
int GetarrItem(int i); // 요소 참조 함수
void SetarrItem(int i, int item); // 저장된 요소 변경 함수
void AddarrItem(int item); // 데이터를 배열에 저장하는 함수
void ShowData();
};
// 매개변수는 배열의 인덱스
int Arr::GetarrItem(int i){
return arr[i];
}
void Arr::SetarrItem(int i, int item){
if(index <= i)
{
cout << "non-existence" << endl;
return;
}
arr[i] = item;
}
void Arr::AddarrItem(int item){
// 배열의 범위를 넘어서지 않는지 검사
if(index >= SIZE)
{
cout << "index is over the arr size" << endl;
return;
}
arr[index++] = item;
}
void Arr::ShowData(){
for(int i = 0; i < index; i++)
{
cout << "arr [ "<< i <<" ]= " << arr[i] << endl;
}
}
int main(void){
Arr arr;
arr.AddarrItem(1);
arr.AddarrItem(2);
arr.AddarrItem(3);
arr.ShowData();
arr.SetarrItem(0,10);
arr.SetarrItem(1,20);
arr.SetarrItem(2,30);
cout << arr.GetarrItem(0) << endl;
cout << arr.GetarrItem(1) << endl;
cout << arr.GetarrItem(2) << endl;
return 0;
}
. 위의 예제를 배열의 인덱스 연산자( [ , ] ) 로 접근할 수 있도록 수정함
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <iostream>
using std::endl;
using std::cout;
const int SIZE=3;
class Arr{
private:
int arr[SIZE];
int index;
public: // i = index
Arr():index(0){}
int GetarrItem(int i);
void SetarrItem(int i, int item);
void AddarrItem(int item);
void ShowData();
int& Arr::operator[](int i);
};
int Arr::GetarrItem(int i){
return arr[i];
}
void Arr::SetarrItem(int i, int item){
if(index <= i)
{
cout << "non-existence" << endl;
return;
}
arr[i] = item;
}
void Arr::AddarrItem(int item){
if(index >= SIZE)
{
cout << "index is over the arr size" << endl;
return;
}
arr[index++] = item;
}
void Arr::ShowData(){
for(int i = 0; i < index; i++)
{
cout << "arr [ "<< i <<" ]= " << arr[i] << endl;
}
}
int& Arr::operator[](int i){
return arr[i];
}
int main(void){
Arr arr;
arr.AddarrItem(1);
arr.AddarrItem(2);
arr.AddarrItem(3);
arr.ShowData();
/*
arr.SetarrItem(0,10);
arr.SetarrItem(1,20);
arr.SetarrItem(2,30);
*/
arr[0] = 10; // arr.operator[](0)
arr[1] = 20;
arr[2] = 20;
/*
cout << arr.GetarrItem(0) << endl;
cout << arr.GetarrItem(1) << endl;
cout << arr.GetarrItem(2) << endl;
*/
cout << arr[0] << endl;
cout << arr[1] << endl;
cout << arr[2] << endl;
return 0;
}
. arr [ 0 ] = 10
에서 , arr[i] 는 L-value로 사용되고 있음. 따라서, 참조에 의한 리턴이 이루어져야 값을 저장할 수 있음
객체 배열 인덱스 연산자 오버로딩
. 객체를 저장할 수 있는 배열 클래스
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <iostream>
using std::endl;
using std::cout;
using std::ostream;
const int SIZE=5;
/*-------------- Point Class ---------------*/
class Point{
private:
int x, y;
public:
Point(int _x=0, int _y=0):x(_x), y(_y){}
friend ostream& operator<<(ostream& os, const Point& p);
};
ostream& operator<<(ostream& os, const Point& p){
os << " [ " << p.x << " , " << p.y << " ] ";
return os;
}
/*-------------- PointArr Class ---------------*/
class PointArr{
private:
Point arr[SIZE];
int index;
public: // i = index
PointArr():index(0){}
void AddarrItem(const Point& item);
void ShowData();
Point& operator[](int i); // 배열 요소 접근 연산자 오버로딩
};
void PointArr::AddarrItem(const Point& item){
if(index >= SIZE)
{
cout << "index is over the arr size" << endl;
return;
}
arr[index++] = item;
}
void PointArr::ShowData(){
for(int i = 0; i < index; i++)
{
cout << "arr [ "<< i <<" ]= " << arr[i] << endl;
}
}
Point& PointArr::operator[](int i){
return arr[i];
}
int main(void){
PointArr arr;
arr.AddarrItem(Point(1,1)); // 임시 객체 형태, 생성되자마자 배열 복사 후, 소멸
arr.AddarrItem(Point(2,2));
arr.AddarrItem(Point(3,3));
arr.ShowData();
arr[0] = Point(10, 10); // arr.operator[](0)
arr[1] = Point(20, 20);
arr[2] = Point(30, 30);
cout << arr[0] << endl;
cout << arr[1] << endl;
cout << arr[2] << endl;
return 0;
}
대입 연산자(=
) 오버로딩
default 대입 연산자
. 멤버 변수 대 멤버 변수의 복사가 이루어지는 =
연산자의 디폴트 오버로딩 = 얕은 복사(shallow copy)
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
36
37
38
39
40
41
#include <iostream>
using std::endl;
using std::cout;
using std::ostream;
class Point{
private:
int x, y;
public:
Point(int _x=0, int _y=0):x(_x), y(_y){}
friend ostream& operator<<(ostream& os, const Point& p);
};
ostream& operator<<(ostream& os, const Point& p){
os << " [ " << p.x << " , " << p.y << " ] ";
return os;
}
/* 자동으로 생성되는 default 대입 연산자 정의
리턴 타입이 Point& 이며, 자기자신(this)를 리턴하는 이유는, p1 = p2 = p3; 와 같은
연속적인 대입연산에 대응하기 위함
Point& Point::operator=(const Point& p)
{
x = p.x;
y = p.y;
return *this;
}
*/
int main(void){
Point p1(1, 2);
Point p2(10, 20);
cout << p1 << endl;
cout << p2 << endl;
p1 = p2; // p1.operator=(p2);
cout << p1 << endl;
return 0;
}
깊은 복사(Deep copy) 대입 연산자
. 디폴트 대입 연산자의 문제점
① 얕은 복사(Shallow copy) : 멤버 변수 값 복사 시, 단순히 포인터 값만 복사하기 때문에, 객체가 소멸되는 시점에 특정 값은 두 번 삭제를 시도하는 문제점을 가짐( 밑의 “LEE” 의 경우 두번 삭제가 호출됨)
② 메모리 유출 : 멤버 변수 복사가 끝나 포인터가 가리키는 위치가 바뀐 값(“KIM”)의 경우, 소멸되지 않고 프로그램이 종료될 때까지 메모리에 계속 남아있게 됨
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream>
#include <string.h>
using std::endl;
using std::cout;
using std::ostream;
class Person{
private:
char* name;
public:
Person(char* _name);
Person(const Person& p);
~Person();
friend ostream& operator<<(ostream& os, const Person& p);
};
Person::Person(char* _name){
name = new char[strlen(_name)+1];
strcpy(name, _name);
}
Person::Person(const Person& p){
name = new char[strlen(p.name)+1];
strcpy(name, p.name);
}
Person::~Person(){
delete[] name;
}
/* 자동으로 생성되는 default 대입 연산자 정의
리턴 타잎이 Point& 이며, 자기자신(this)를 리턴하는 이유는, p1 = p2 = p3; 와 같은
연속적인 대입연산에 대응하기 위함
Person& Person::operator=(const Person& p)
{
name = p.name;
return *this;
}
해당 default 대입 연산자는 sallow copy을 수행 (멤버 변수 간 복사)
- 두 개의 객체 사용 시, 특정 멤버 변수는 소멸자에 의해 메모리 공간 삭제 콜이 두번 일어남(에러발생)
- 두 개의 객체 사용 시, 특정 멤버 변수가 지니고 있던 값을 가르키는 데이터가 변경되어, 데이터는 있으나, 소멸되지 않는 메모리 공간 발생
*/
// deep copy 를 수행하는 default 대입 연산자 정의
Person& Person::operator=(const Person& p)
{
delete [] name; // 디폴트 대입 연산자의 두 번째 문제점인, 메모리 유출을 방지
name = new char[strlen(p.name)+1]; // 깊은 복사 (deep copy)
strcpy(name, p.name); // 깊은 복사 (deep copy)
return *this;
}
ostream& operator<<(ostream& os, const Person& p){
os << p.name;
return os;
}
int main()
{
Person p1("KIM");
Person p2("LEE");
cout << p1 << endl;
cout << p2 << endl;
p1 = p2; // shallow copy
cout << p1 << endl;
return 0;
}
. 생성자 내에서 메모리 공간을 동적 할당하게 되면, 할당된 메모리를 해제하는 용도의 소멸자를 정의해야함, 복사 생성자와 대입 연산자도 깊은 복사를 하도록 정의해야 함