열혈 강의 10장

static복습하고 오기. 전역함수로 선언(friend 선언 이해하기)


왜 연산자 오버로딩인가

함수 오버로딩이란 이르이 같아도 전달 인자에 따라 호출되는 함수가 달라지는 것

int a = 1+2;             --> 정수형 변수

Point p3 = p1+p2;  --> 객체

--> 같은 +인데도 피연산자의 종류에 따라서 다른 +기능을 한다.


1. 객체를 리턴하는 함수

1. AAA& GetName(){ return a; }

2.AAA GetName() { return a; }

--> 차이가 뭐지


class Point {

private:

int x, y;


public:

Point(int _x=0, int _y=0) : x (_x), y(_y) P{}

void show();

Point& operator++();

friend Point& operato--r(Point & _p);

}


void Point::show() {

cout <<x<<"  "<<y<<endl;

}


Point& Point::operator++() {  --> 왜 레퍼런스로 리턴을 받는가? 

x++;

y++;

return *this;  --> this는 포인터 *가 붙었기 때문에 자기자신을 리턴

}


Point& operator--(Point& _p){  --> 왜 참조로 리턴을 하는가?

_p.x--;

_p.y--;

return _p;

}


int main(){

Point p(1,2);

++p;

p.show();   ///2,3


--p

p.show();    ///1,2


++(++p);    ///3,4

p.show();


--(--p);

p.show();    ///1,2

}

----------------------------------------------------------------------------------문제 제기


Point Point::operator++(){

x++;

y++;

return *this;

}


--> 참조로 리턴을 받지 않는다면?? --> 나 자신을 복사해서 리턴을 한다.  --> 리턴과 동시에 새로운 객체를 만들게 된다. (복사본)

--> ++(++p)  --> 밑줄 친 부분이 p자기 자신이 아니라 복사본이 오게 된다.

--> 그 결과 p.show()에는 2,3 즉, p는 ++연산을 한번만 하게 되고, 괄호 밖의 ++연산은 p의 복사본에 하게된다. 

--> 복사생성자 호출조건 case3 참고


--> 조로 리턴을 받는다면?? --> ++(++p) --> 밑줄 친 부분에 복사본이 아닌 p의 레퍼런스가 위치하게 된다.

--> 그 결과 p에 ++연산이 두번 실행된다.


--> 함수를 void로 선언하지않고 Point&로 선언하는 이유이다.


check!! 

--> p3    =  p1   +   p1;

      p3    =  리턴된 객체  ---> 복사생성자에 의해 멤버 대 멤버 복사가 일어난다.


2. 단항 연산자 오버로딩 전위 후위


++p --> p.operator++();

p++ --> p.operator++(int);   --> int는 단순히 구분하기 위한 용도


Point operator++(int){

Point temp(x,y);            --> 증가시키기 전의 값을 저장

x++;

y++;

return temp;

}                           --> 참조를 리턴하지 않는 이유 --> 지역객체이기 때문에 레퍼런스로 리턴할 수 없다.


int main(){

Point p(1,2);

(p++).show();    --> temp를 리턴받았기 때문에 1 2 출력

p.show();           --> 증가된 x, y

}

--> 참조가 불가능 하기 때문에 (p++)++ 는 불가능



3. 교환 법칙


Point p(1,2);

p+3; ---> 가능

3+p; ---> 불가능

가능하게 하려면?


class Point{

private:

int x,y;

public:

Point operator+(int val);

friend Point operator+(int val, Point& p);   ---> friend는 연산자 오버로딩 외에는 사용하지 말자.

}


Point::Point operator+(int val){

Point temp(x+val, y+val);

return temp;

}


Point operator(int val, Point& p){                        Point operator(int val, Point& p){

Point temp(p.x+val, p.y+val);        ---->          return p+val;                                   --> 바꾸어주는 기능으로도 구현할 수 있다.

return temp;                                                 }

}


전역함수로 선언해줌으로써 해결된다.


4. 임시 객체


Point p(1,2);

Point (1,2);   ---> 임시 객체 생성방법  --> 임시 객체는 생성된 후 다음 줄에서 바로 소멸된다.


임시 객체를 만드는 이유는 뭐냐?

-->임시 객체는 생성과 소멸을 최적화 시킨다. 임시 객체를 쓸 수 있는 부분에서는 써주는 것이 좋다.

교환법칙의 예에서 임시 객체를 사용할 수 있는 부분을 찾아보자.


Point Point::operator+(int val){

return Point(x+val, y+val);  ---> 소멸이 되기 전에 리턴!!

}


5. 대입 연산자 오버로딩


class Point{ ... };

int main(){

Point b(1,2);

Point a = b   --->  AAA a(b);  ---> 복사생성자에 의한 대입연산은 생성될 땐 가능하지만


Point a(1,2);

Point b(10,20);


a=b;   ----> a(b) ---> 객체 생성 문장이 아님 --> 복사생성자에 의한 대입 연산 불가능

but 디폴트 대입 연산자가 제공되기 때문에 가능하다.

}


------------------------------------------------------------------디폴트 대입연산자 ex


Point& operator=(const Point& p){

x=p.x;

y=p.y;

return *this;

}


대입 연산자의 문제점


class Person{

private:

char* name;

public:

Person(char* _name){

name = new char[strlen(_name)+1];

strcpy(name,_name);

}

Person (const Person& p){

name = new char[strlen(p.name)+1];

strcpy(name,p.name);

}

~Person(){ delete[] name }

};


int main(){                                        스택           힙

Person a("ddd");                          a ---->  ddd

Person b("fff");                            b ---->  fff


a=b;   --------------->> 문제 발생

}


b가 가진값을 a에 복사해준다. a가 b가 가리키는 fff를 가리키게 된다. (복사생성자의 문제와 유사)

디폴트 대입연산자는 얕은 복사가 가지지 않는 또 다른 문제 하나를 더 가지고 있다.

--> 메모리 유출이 발생한다.

--> a가 원래 가리켰던 ddd가 힙에 계속 남아있게 된다.


--> 올바르게 동작하도록 바꿔보자!

1. a가 가리키던 영역을 해제시켜준다.

2. 메모리 할당을 다시해서 b와 같은 문자열을 할당해준다.


Person& operator=(const Person& p){

delete []name;                                              --> 메모리 해제

name = new char[strlen(p.name)+1];            --> 깊은 복사에 의한 할당

strcpy(name,p.name);


return *this;

}


Point!!

--> Point&을 통해 자기자신을 리턴하는 이유 --> p3 = p1 + p2; 와 같은 연산을 수행하기 위해

--> 생성자에서 동적할당을 하게 되면  1. delete

    2. 깊은 복사를 하는 복사생성자.

    3. 해제, 깊은 복사를 하는 대입 연산자.

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

[C++] 복사생성자 정리  (0) 2016.12.07
[C++] 레퍼런스 정리  (0) 2016.12.07
[C++] virtual 소멸자, virtual table // 열혈강의  (0) 2016.12.05
[C++] 상속에 대하여 // 열혈강의  (0) 2016.12.01
[C++] 기초4 // 열혈강의  (0) 2016.12.01

+ Recent posts