C/C++/C++

연산자 오버로딩

꿀꿀이냐옹이 2009. 10. 9. 00:06
반응형

1.연산자 오버로딩의 의미와 방법

 

(1)연산자오버로딩의 의미

-기본자료형 - int, 대입연산,사칙연산가능

-사용자정의형(구조체) - 대입연산,사칙연산 불가능

 

사용자정의형으로도 기본자료형처럼 쓸 수도록 하기 위해서 등장한 것이 연산자오버로딩이다.

 

cf)C++에 대한 전문가의 평가 : 문법을 너무 남용하는 측면이 있는 문법요소 :friend, 연산자오버로딩

 

(2)오버로딩의 방법

<1>멤버함수에 의한 오버로딩

 

class Point

{

 private:

  int x,y;

 public:

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

  void ShowPosition()

 {

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

 }

 void operator+ (int val)

 {

  x += val;

  y += val;

 }

 

 Point operator+(const Point& p)

 {

  Point temp(x + p.x , y + p.y);

 

  return temp;

 }

};

 

 

//"연산자만으로 함수가 호출되는 것을 허용하겠다."

 

int main()

{

 

 Point p(3,4);

 p.ShowPosition();

 

 p.operator+(10);

 p.ShowPosition();

 

 Point p1(1,2);

 Point p2(2,1);

 Point p3 = p1 + p2; //operator+함수를 찾는다.

//함수로 존재한다.멤버함수에 연산자오버로딩이 존재한다. p1.operator(p2);로 변환된다.

 p3.ShowPosition();

 

 return 0;

}

 

 

<2>전역함수에 의한 오버로딩

 

class Point

{

 private:

  int x,y;

 public:

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

  void ShowPosition()

 {

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

 }

 void operator+ (int val)

 {

  x += val;

  y += val;

 }

friend Point operator+(const Point& p1,const Point& p2);

 

};

 

 Point operator+(const Point& p1,const Point& p2)

 {

  Point temp(p1.x + p2.x , p1.y + p2.y);

 

  return temp;

 }

 

//"연산자만으로 함수가 호출되는 것을 허용하겠다."

 

int main()

{

 

 Point p1(1,2);

 Point p2(2,1);

 Point p3 = p1 + p2; //operator+함수를 찾는다.함수를 찾는다.

//전역에 +연산자오버로딩이 존재한다. operator(p1,p2);로 변환된다.

 p3.ShowPosition();

 

 return 0;

}

 

2.단항 연산자의 오버로딩

(1)연산자 오버로딩 방법

 

++p

멤버함수라면==> p.operator++();

전역함수라면==>operator++(p);

 

class Point

{

 private:

  int x,y;

 public:

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

  void ShowPosition()

 {

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

 }

Point& operator++() //참조를 반환하는 이유 : ++(++p);와 같은  연산을 허용하기 위해서

//만약에 반환값이 Point라면 ++p의 반환값이 p의 복사본p2가 리턴되고 , p복사본 p2에 operator++가 호출된다.

//원본p는 한번만 증가하게되고 복사본만이 두 번 증가하게 된다.따라서 값에 의한 반환은 문제가 있다.

//코드의 안정성을 위해서 void 반환 코드는 쓰지말자.

{

  x++;

  y++;

 

  return *this;

}

friend Point operator+(const Point& p1,const Point& p2);

 

};

 

Point& operator--(Point& p)

 {

  p.x--;

  p.y--; 

 

  return p;

 }

 

 

int main()

{

 Point p(1,2);

 ++p;

 p.ShowPosition();

 

 --p;

 p.ShowPosition();

 

 ++(++p); //p.operator++()으로 반환값으로 p의 참조가 반환한다. 다시 ++p의 형태가 된다.

//다시 p.operator++()으로 반환값으로 p를 반환한다; 따라서 x,y값이 두 번 증가한다.

 p.ShowPosition();

 

--(--p);//operator--(p)는 p의 참조를 반환하고

//p의 참조에 다시 operator--(p)가 수행되어 x,y값이 각가 두 번 감소한다.

p.ShowPosition();

 

return 0;

}

 

(2)선 연산과 후 연산의 구분

 

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

p++ ==> p.operator++(int); //int 형 ++연산자가 앞에 붙였느냐 뒤에 붙였느냐 구분하기 위해서 쓰인다.

 

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

p-- ==> p.operator--(int);

 

class Point

{

 private:

 int x,y;

 public:

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

 

 void ShowPosition()

 {

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

 }

 

 Point& operator++() //선연산

 {

   x++;

   y++;

 

   return *this;

 }

 

 Point operator++(int) //후연산

 {

   Point temp(x,y); //지역객체이므로 참조로 리턴할 수 없다.

   x++;

   y++;

 

  return temp; //증가하기 이전의 복사본을 리턴한다.

 }

};

 

int main()

{

 Point p1(1,2);

 (p1++).ShowPosition(); //1,2

 p1.ShowPosition();//2,3

 

 Point p2(1,2);

 (++p2).ShowPosition();//2,3

 

 return 0;

}

 

(3)연산자오버로딩의 용도

-STL, 대입연산자

 

3.교환법칙 해결

 

class Point

{

 private:

 int x,y;

 public:

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

 

 void ShowPosition()

 {

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

 }

 

 Point operator+(int val)

 {

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

  

   return temp;

 } 

 friend Point operator+(int val,Point& p);

};

 

//첫번째 방법

Point operator+(int val , Point& p)

{

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

  

   return temp;

}

 

//두 번째 방법 (순서 바꾸기)

Point operator+(int val , Point& p)

{

 return p + val;

}

 

int main()

{

 Point p1(1,2);

 

 Point p2 = p1 + 3; //p1.operator+(3);

 Point p3 = 3+ p1; //compiler error  멤버버전을 호출한다면3.operator+(p);이므로  문제가 있다.

   //이런 교환법칙이 적용되도록 하려면 전역함수로 만들면 가능하다. operator+(3,p);가 될 것이다.

 //전역함수로만 해결가능하다.

 p2.ShowPosition();

 

 return 0;

}

 

4.임시객체의 생성

 

#include <iostream>

 

using std::endl;

using std::cout;

 

class AAA

{

 char name[20];

 public:

  AAA(char* _name)

 {

   strcpy(name,_name);

   cout<<name<<" 객체 생성"<<endl;

 }

 ~AAA()

 {

  cout<<name<<" 객체 소멸"<<endl;

 }

};

 

int main()

{

 AAA aaa("aaa obj");

 

 cout<<"임시 객체 생성 전 "<<endl;

 

 AAA("Temp obj");//객체는 생성되지만 이름이 없다.다음 줄로 넘어가면서 바로 소멸된다.

 

 int a = 3 + 4;

 

 cout<<"임시 객체 생성 후"<<endl;

 

 return 0;

}

 

-생성방법 : Point (3,4);

-효과 : 생성과 소멸이 컴파일러에 의해서 최적화된다.

 

-임시 객체 생성의 적용

 

Point operator+(int val , Point& p)

{

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

  

   return temp;

}

 

===>

Point operator+(int val , Point& p)

{

   return Point (p.x + val , p.y + val); //임시 객체 생성후 바로 리턴

}

 

 

5.cout,cin,endl의 원리 이해

 

#include <stdio.h>

 

namespace mystd

{

 char *endl ="\n";

 

 class ostream

 {

   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;

}

 

<<   ,  >>연산자의 오버로딩

 

#include <iostream>

 

using std::cout;

using std::endl;

 

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클래스의 privat멤버에 접근하기 위해서 friend로 선언한다.

};

 

ostream& operator<<(ostream& os, const Point p)

{

  os<<"["<<p.x<<", "<<p.y<<"]"<<endl;

 return os;

}

 

int main()

{

 Point p(1,3);

 

 cout<<p<<"hello"<<endl; //cout.operator<<("hello")는 불가능하다. 표준라이브러리를 건드려야 하므로.

 

  return 0;

}

 

6.인덱스 연산자 []와 오버로딩

 

arr[i] ==> arr.operator[](i)

 

 

Arr arr;//객체 생성 내부에 배열이 선언되어 있어야 한다.

 

int& operator[](int i) //참조를 반환해야 arr[1] = 10;같이 값을 저장할 수 있다.

{

  return arr[i];

}

 

 

7.대입연산자 오버로딩

 

(1)디폴트 대입 연산자

-멤버 대 멤버 복사

 

-첫번째 case

A a = b; //A a(b);로 묵시적으로 변환된다. (디폴트 복사생성자)

 

-두번째 case

A a;

B b;

a = b; //a(b);(x) -객체생성 문장이 아니다.  a.operator=(b);로 해석된다 .(디폴트대입연산자)

 

두가지 case를 혼동해서는 안된다.

 

 

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클래스의 privat멤버에 접근하기 위해서 friend로 선언한다.

 

   //디폴트 대입연산자를 구현해보면 아래와 같다.

    Point& operator=(const Point &p)

   {

      //멤버 대 멤버 복사를 한다.

      x= p.x ;

      y= p.y;

      return *this;

   }

};

 

ostream& operator<<(ostream& os, const Point p)

{

  os<<"["<<p.x<<", "<<p.y<<"]"<<endl;

 return os;

}

 

int main()

{

 Point p1(1,3);

 Point p2(10,30);

 cout<<p1<<endl;

 cout<<p2<<endl;

 

 p1 = p2; //p1.operator=(p2);와 동일하다. 디폴트 대입연산자가 호출된 것이다.

 cout<<p1<<endl;

 

 return 0;

}

 

(2)대입연산자를 정의할 필요한 경우

 

class Person

{

  private:

   char* name;

  public:

   Person(char* _name)

  {

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

    strcpy(name ,_name);

  }

  ~Person()

  {

    if(name) delete name;

  }

   Person(const Person &p)

  {

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

    strcpy(name ,p.name);

  }

//아래와 같이 대입연산자를 정의해주면 문제가 해결된다.

  Person& operator=(const Person& p)

  {

      delete []name; //이전 메모리 할당 영역 삭제(1단계)

      //다시 메모리를 할당한다(2단계)

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

      //복사를 한다.(3단계)

      strcpy(name,p.name);

  

      return *this;

  }

};

 

int main()

{

 Person p1("kkk"); //내부적으로 동적메모리 할당이 일어난다.

 Person p2("hhh"); //내부적으로 동적메모리 할당이 일어난다.

 cout<<p1<<endl;

 

 p1 = p2;  //얕은 복사(shallow copy) ,디폴트 대입연산자가 호출된다.

//p1.operator=(p2); p2의 할당메모리는 포인터를 잃어버려서 메모리 누출이 발생한다.

 

 cout<<p1<<endl;

}

 

반응형