728x90

C++ 복사 대입 연산자는 뭐고 언제 호출이 될까?


대입 연산자는 자신과 같은 타입의 다른 객체를 대입받을 때 사용하는 연산자이다.
객체 자체와 직접적인 연관이 있기 때문에 클래스의 멤버 함수로만 정의할 수 있으며 전역 함수로는 정의할 수 없다.
정적 함수로도 만들 수 없고 반드시 일반 멤버 함수로 만들어야 한다.

대입 연산자는 연산자 오버로딩의 결과로써 실행된다.
따로 선언하지 않을 경우 default로 얕은 복사를 하는 대입 연산자를 컴파일러가 자동으로 만든다.

포인터를 가지는 경우 복사생성자(Copy Constructor)와 대입연산자(Copy Assignment Operator)를 반드시 정의해야 한다.


예제1. (테스트 : Visual Studio 2019 Community)

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

class Person {
    char* name;
    char* phone;
    int age;
public:
    Person();
    // VC++에서 char* _name은 문자열 상수를 직접 입력으로 받지 못함
    Person(const char _name[], const char _phone[], int _age);
    ~Person();
    Person(const Person& p);
    Person& operator=(const Person& p);
    void ShowData(); // 선언
};

Person::Person() {
    cout << " 매개변수 없는 생성자 호출" << endl;
    name = NULL;
    phone = NULL;
    age = 0;
}

Person::Person(const char _name[], const char _phone[], int _age) {
    cout << " 매개변수 3개 생성자 호출" << endl;
    name = new char[strlen(_name) + 1]; // NULL문자를 고려하여 +1만큼 할당
    strcpy_s(name, strlen(_name) + 1, _name);

    phone = new char[strlen(_phone) + 1];
    strcpy_s(phone, strlen(_phone) + 1, _phone);

    age = _age;

    cout << " name 주소 : " << (void*)name << ", phone 주소 : " << (void*)phone << endl;
}

Person::~Person() { // DeAllocate the heap
    cout << " 소멸자 호출" << endl;
    delete[]name;
    delete[]phone;

    cout << " name 주소 해제 : " << (void*)name << ", phone 주소 해제 : " << (void*)phone << endl;
}

Person::Person(const Person& p) : age(p.age) {
    cout << " 복사 생성자 호출" << endl;
    name = new char[strlen(p.name) + 1]; // NULL문자를 고려하여 +1만큼 할당
    strcpy_s(name, strlen(p.name) + 1, p.name); // 깊은 복사
    // The statement name = new char; will create the new heap location
    // and then copies the value of obj content to new heap location.

    phone = new char[strlen(p.phone) + 1];
    strcpy_s(phone, strlen(p.phone) + 1, p.phone);

    cout << " name 주소 : " << (void*)name << ", phone 주소 : " << (void*)phone << endl;
}

Person& Person::operator=(const Person& p) {
    cout << " 복사 대입 연산자 호출" << endl;
    if (this != &p) {
        delete[]name;
        delete[]phone;

        name = new char[strlen(p.name) + 1]; // NULL문자를 고려하여 +1만큼 할당
        strcpy_s(name, strlen(p.name) + 1, p.name); // 깊은 복사

        phone = new char[strlen(p.phone) + 1];
        strcpy_s(phone, strlen(p.phone) + 1, p.phone);

        age = p.age;

        cout << " name 주소 : " << (void*)name << ", phone 주소 : " << (void*)phone << endl;
    }
    return *this;
}

void Person::ShowData() { // 클래스 외부에서 클래스 멤버 함수 정의
    cout << " name : " << name << ", phone : " << phone << ", age : " << age << endl;
}

int main() {
    cout << endl; // 출력 구분 목적
    Person p1("홍길동", "010-1234-5555", 34);
    p1.ShowData();

    cout << endl;
    Person p2(p1); // 복사 연산자 호출
    p2.ShowData();

    cout << endl;
    Person p3;
    p3 = p1; // 대입연산자 호출된다. p3.operator=(p1);
    p3.ShowData();

    // 객체가 소멸되는 순서는 객체가 생성된 순서의 반대다.
    cout << endl;
}


실행결과


깊은 복사가 2번(복사생성자, 복사 대입 생성자) 일어나는 것이 좋은 것일까?

728x90
블로그 이미지

Link2Me

,