대입연산자 오버로딩

기본 대입연산자

잠시 복사생성자에 대해 복습합니다. 복사생성자는 다음과 같은 특성이 있다.

  • 정의하지 않으면 기본 복사 생성자가 삽입된다.

  • 기본 복사생성자는 얕은복사를 진행한다.

  • 동적할당을 하는 멤버가 있을 경우 깊은복사를 하기위해 직접 정의해야 한다.

대입연산자도 다음과 같은 특성이 있다.

  • 정의하지 않으면 기본 대입연산자 가 삽입된다.

  • 기본 대입연산자는 얕은복사를 진행한다.

  • 동적할당을 하는 멤버가 있을 경우 깊은복사를 하기위해 직접 정의해야 한다.

앞서 복사생성자와 같은 문제가 발생하므로 객체 맴버중에 동적할당을 하여 깊은 복사가 필요한 경우에 대입연산자를 다중정의 해야 한다.

기본 대입연산자의 문제점

#include <iostream>
#include <cstring>
using namespace std;

class Person {
  char *name;
  int age;
public :
  Person() : age(0){
    name = NULL; // 빈객체가 소멸될때 에러가 delete연산을 막음
  }
  Person(const char* name, int age)
    : age(age)
  {
    this->name = new char[strlen(name) + 1];
    strcpy(this->name, name);
  }

  ~Person() {
    cout << name << " ~Person() 호출" << endl;
    delete[] name;
  }

  void showInfo() {
    cout << "이름 : " << name << endl;
    cout << "나이 : " << age << endl;
  }
};

int main() {
  Person man1("남기용", 43);
  Person man2;

  man2 = man1;
  man1.showInfo();
  man2.showInfo();
  return 0;
}

기본 대입연산자는 앝은복사를 진행하기 때문에 멤버변수 name이 동적할당되어 소멸자에 의해서 두번 삭제가 되기 때문에 오류가 발생된다.

대입연산자 오버로딩

다음과 같이 Person 객체에 대입연산자 오버로딩을 하여 문제를 해결할 수 있다.

Person& operator=(const Person& ref) {
  age = ref.age; // 정적멤버는 일반 대입한다.

  delete[] name; // 메모리 누수를 막기위한 메모리 해제
  name = new char[strlen(ref.name) + 1]; // 새로 할당받는다
  strcpy(name, ref.name);
  return *this;
}

상속구조에서 대입연산자 호출

파생 클래스의 대입연산자에서 명시하지 않으면 기초클래스의 대입연산자는 호출되지 않는다.

#include <iostream>
#include <cstring>
using namespace std;

class Person {
  char *name;
  int age;
public :
  Person() : age(0){
    name = NULL; 
  }
  Person(const char* name, int age)
    : age(age)
  {
    this->name = new char[strlen(name) + 1];
    strcpy(this->name, name);
  }

  virtual ~Person() {
    delete[] name;
  }

  virtual void showInfo() {
    cout << "이름 : " << name << endl;
    cout << "나이 : " << age << endl;
  }

  Person& operator=(const Person& ref) {
    age = ref.age;

    delete[] name;
    name = new char[strlen(ref.name) + 1];
    strcpy(name, ref.name);
    return *this;
  }
};

class Student : public Person {
  char * school;
public :
  Student() {
    school = NULL;
  }

  Student(const char* name, int age, const char* school) 
    : Person(name, age)
  {
    this->school = new char[strlen(school) + 1];
    strcpy(this->school, school);
  }

  ~Student() {
    delete[] school;
  }

  virtual void showInfo() {
    Person::showInfo();
    cout << "학교 : " << school << endl;
  }

  Student& operator=(Student& ref) {
    delete[] school;
    school = new char[strlen(ref.school) + 1];
    strcpy(school, ref.school);
    return *this;
  }
};

int main() {
  Student man1("남기용", 43, "방송통신대학교");
  Student man2;

  man2 = man1;
  man1.showInfo();
  man2.showInfo();
  return 0;
}

Student 클래스의 operator=함수에서 Person 클래스의 대입이 이루어 지지 않기 때문에 man2.name은 NULL 값을 가지게 될 것이다. 이러한경우 명시적으로 Person객체의 대입연산자를 호출하여야 한다.

Studend 클래스의 대입연산자를 다음과 같이 변경하자.

Student& operator=(Student& ref) {
  Person::operator=(ref); // 기초 클래스의 대입연산자 호출

  delete[] school;
  school = new char[strlen(ref.school) + 1];
  strcpy(school, ref.school);
  return *this;
}

Last updated