본문 바로가기

Language/C++

[C++] 복사 생성자, 연산자 오버로딩(Copy Constructor, Operator Overloading)- 컴도리돌이

728x90

outline

copy constructor

friend, static

Operator overloading


복사 생성자(Copy constructor)

  • 같은 클래스의 다른 객체를 사용하여 또 다른 새로운 객체에 복사하여 생성한다.

  • 복사 생성자(copy constructor)는 값을 가진 객체(object)를 반환할 때 사용되는데, 이때 또 다른 값을 가진 객체(object)를 변수(argument)를 받아서 copy를 해준다.

ClassName(const ClassName& old_obj); 

 


class Point{
   public:
 	double x, y;
 	// ...
};
Point GetScaledPoint(double scale, Point p) {
   Point p_new;
 	p_new.x = p.x*scale; p_new.y = p.y*scale;
 	return p_new;
}
int main(int argc, char* argv[]){
   Point p1(0.1, 0.2);
   Point p2 = GetScaledPoint(2.0, p1);
   Point p3 = p1;
   Point p4(p1);
   return 0;
}
Point p3 = p1;
Point p4(p1);
  • 위에 예시를 보면 "Point p3 = p1;", "Point p4(p1);"처럼 형태로 객체를 생성하는데, 생성된 객체를 복사 생성자(copy constructor)이라 부른다.

디폴트 복사 생성자(Default copy constructor)

  • 복사 생성자(copy constructor)를 따로 정의하지 않았다면, 컴파일러에서 기본적으로 복사 생성자가 암시적으로 생성되는데 default copy constructor이라 한다.

class MyString{
private:
  int len;
  char *str;
public:
 MyString(const char *s = ""){
   len = strlen(s);
   str = new char[len+1];
   strcpy(str, s);
   }
 ~MyString(){delete[] str;}
 void Print() { cout << str << endl;}
};

int main(){
  MyString s1 = "Hanyang";
  MyString s2 = s1;
  
  s1.Print();
  s2.Print();
  
  return 0;
}
  • 위에 예시처럼 객체에서 포인터를 쓸 경우 문제가 발생한다. s1에서는 "Hanyang"이라는 문자열을 Heap 영역에 메모리를 할당받고 문자열을 저장한다. 하지만 s2에서는 s2 = s1;로 설정하면 "Hanyang"이라는 값을 단순히 할당받은 메모리를 포인터로 가리키기만 한다. 즉 s2 자체에서 메모리를 할당받은 게 아니라 단순히 s1에서 할당한 메모리 공간을 가리키기만 한다.

  • 문제 : s1에서 destructor을 하면 메모리 공간을 delete를 하지만, s2는 자신이 가리킨 공간이 이미 delete가 되어있기 때문에 delete 할 공간이 사라지게 된다.

 

재정의한 복사 생성자(User-defined copy constructor)

class MyString{
private:
  int len;
  char *str;
public:
 MyString(const char *s = ""){
   len = strlen(s);
   str = new char[len+1];
   strcpy(str, s);
   }
 MyString(const MyString &s){ /*redefine copy constructor*/
   len = s.len;
   str = new char[len+1];
   strcpy(str, s.str);
 ~MyString(){delete[] str;}
 void Print() { cout << str << endl;}
};
  • 위에 예시처럼 제대로 된 copy constructor을 만들어주면 s2에서도 Heap 영역에 별도의 메모리 공간을 할당받을 수 있다.


Friend Class and Function

class ClassA {
private:
   int var_;
   friend class ClassB;
   friend void DoSomething(const ClassA& a);
};
class ClassB {
   // ...
   void Function(const ClassA& a) { cout << a.var_; } // OK.
};
   void DoSomething(const ClassA& a) { cout << a.var_; } // OK.
  • A class의 private 영역에 다른 class를 friend로 선언하면, 다른 class는 A class의 private member를 포함한 모든 member에 접근할 수 있다. 

정적 멤버(Static members)

class Point{
  private:
    int x, y;
    static int count;
  public:
    Point(int a=0, int b=0) : x(a), y(b) {count ++;}
    ~Point() { cout << x << " " << y << endl;}
    static int GetCount() { return count;} /*1*/
};
int Point::count = 0; /*4*/

int main()
{
  cout << Point :: GetCount() << endl; /*2,3*/
  Point P1(1,2);
  return 0;
}
  • static member은 class 내부에 있으며 class의 객체에 의해 공유된다. static member 함수는 오로지 static member에 대해 접근할 수 있다.

  • static member 함수는 virtual이 될 수 없다.(virtual은 instance마다 생기는 function table이 있어야 하는데 static member은 instance와 무관하다. -> class 전체라 생각하자, c언어의 global 변수라 생각하자.)

  • static member은 class name 또는 object name으로 통해 접근할 수 있다.

  • static member 변수는 class 밖에 정의되어야 한다. ( 정의가 안되면 link error가 발생한다. 물론 여러 개 써도 link error가 발생한다.)


연산자 오버 로딩(Operator Overloading)

  • 기존의 제공하고 있는 연산자를 재정의하여 클래스로 사용하는 것이다.

operator op(arguments)

 

멤버 함수에서 연산자 오버 로딩(operator overloading by member function)

#include <iostream>

using namespace std;

class Box{
  private:
    int x, y, z;
  public:
    Box(int a = 0, int b = 0, int c = 0) : x(a), y(b), z(c) {}
    Box operator+ (const Box box) { return Box(x+box.x, y+box.y, z+box.z);}
    void Print(){ cout << x << " " << y << " " << z << endl;}
};

int main(){
    Box B1(1,1,1);
    Box B2(2,2,2);
    Box B3 = B1.operator+(B2);
    Box B4 = B1+ B2;
    return 0;
}
  • 위에 예시를 보면 B3를 B1.operator+(B2)로 assign을 시켜서 새로운 객체를 만들었다. 여기서 B1.operator+(B2) 연산은 B1 + B2 연산과 같다. 만약에 객체와 + 연산이 아닌 다른 type과의 연산에서는 어떻게 될까? 예를 들면 2.0 + B2 같은 경우에는 non-member operator overloaded 함수를 사용해야 한다.

함수 밖에서 정의된 멤버 함수에 대한 연산자 오버 로딩(Operator overloading by nonmember function)

#include <iostream>

using namespace std;

class Point{
  private:
    int x, y
  public:
    Point(int a = 0, int b = 0) : x(a), y(b){}
    friend Point operator+ (int a, Point &Po);
    void Print(){ cout << x << " " << y << " " << endl;}
};

Point operator+(int a, Point &Po){
  return Point(a + Po.x, a + Po.y);
}

int main(){

    Point P1(2,2);
    int a = 2;
    Point P3 = a + P1; /*Point P3 = operator+(a, P1); */
    return 0;
}
  • 위에 예시처럼 operator 함수를 class의 member로 지정하지 않고 class 밖에 새롭게 함수처럼 사용을 한다. 하지만 class 밖에 있는 함수이기 때문에 point 클래스의 private 값에 접근하기 위해서 해당 함수를 friend 함수를 사용해서 private 값에 접근할 수 있게 설정한다.