반응형

 

※ 들어가기에 앞서 참조자와 복사 생성자에 대한 사전 지식이 필요하니 다음 글을 참조

 

C++ 생성자, 소멸자 (+ const)

들어가기에 앞서... const - const 변수 : 해당 변수를 정보 변경이 불가능한 상수로 변환. - const 함수 : 해당 함수에서 const의 위치에 따라 정보의 변환을 막거나 반환값을 상수로 바꾼다. 1. const 변수

srdeveloper.tistory.com

 

C++ 참조자(Reference)

참조자 (Reference) - 할당된 어떤 하나의 메모리 공간에 다른 이름을 부여하는 것이다. ※ 별명이라고 생각해도 좋다. - 참조자는 변수 앞에 &를 붙여줌으로써 선언할 수 있다. ※ 이미 선언된 변수

srdeveloper.tistory.com


깊은 복사, 얕은 복사

- 깊은 복사 : 값이 복사되는 복사

- 얕은 복사 : 참조 값이 복사되는 복사

 

 

 

디폴트 복사 생성자의 문제점

- 앞서 생성자, 소멸자 포스팅에서 복사 생성자는 정의하지 않아도, 디폴트 복사 생성자가 자동으로 삽입된다.

- 하지만, 반드시 복사 생성자를 정의해야 하는 경우가 있다.

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;

class Test
{
private:
	
    char* cArr;
public:
    Test(const char* cArr)
    {
        int len = strlen(cArr) + 1;
        this->cArr = new char[len];
        strcpy(this->cArr, cArr);
    }
    void Show()
    {
        cout << "cArr : " << cArr << endl;
    }
    ~Test()
    {
        delete cArr;
        cout << "Called destructor" << endl;
    }
};

void main()
{
    Test t1("Test string");
    Test t2 = t1;
    t1.Show();
    t2.Show();
    
    //결과
    //cArr : Test string
    //cArr : Test string
    //Called destructor
}
  • 위와 같이 char 포인터를 디폴트 복사 생성자로 복사가 일어나게 되면 얕은 복사가 일어난다.
  • 즉, 참조값이 복사가 되기 때문에 t2객체와 t1객체의 cArr 포인터가 참조하는 문자열은 같다고 볼 수 있다.
  • 그렇기 때문에 어떤 객체의 소멸자에서 cArr이 참조하는 문자열을 delete로 할당해제 하면 남은 객체에서는 이미 지워진 문자열을 대상으로 delete 연산을 하기 때문에 문제가 된다.
  • 따라서 복사 생성자를 정의할때에는 이러한 문제가 발생하지 않도록 신경써야한다.

 

 

 

복사 생성자의 호출 시점

- 복사 생성자의 호출 시점은 3가지로 구분된다.

  • 기존에 생성된 객체를 이용해서 새로운 객체를 초기화할 때
SomeClass sc1;
SomeClass sc2 = sc1;
  • Call by value 함수호출 과정에서 객체를 인자로 전달할 때
SomeClass Func(SomeClass sc)
{
    ...
}

void main()
{
    SomeClass sc;
    Func(sc)	// 호출하게 되면 매개변수 sc가 할당되는 동시에 초기화
}
  • 함수에서 객체를 참조형으로 반환하지 않을 때
SomeClass Func(SomeClass sc)
{
    return sc	// 반환할 때 메모리 공간이 할당되면서 동시에 초기화
}

void main()
{
    SomeClass sc1;
    SomeClass sc2 = Func(sc1);
}

  ※ 즉, 위 코드에서 반환되는 sc의 객체는 인수로 받은 sc가 아니라 '임시객체'가 만들어져 이 '임시객체'에서 복사 생성자가 호출되어 return에 명시된 객체가 인자로 전달된다.

 

 

 

임시객체, 소멸시점

- 클래스 외부에서 객체의 멤버함수에 접근하는 방법은 다음 세가지이다.

  • 객체에 붙여진 이름
  • 객체의 참조 값 (임시객체)
  • 객체의 주소 값

- 임시객체가 생성된 위치에는 임시객체의 참조 값이 반환된다.

- 그렇기 때문에 다음과 같은 구성이 가능하다.

#include <iostream>
using namespace std;

class Test
{
private:
    int n;
public:
    Test(int n)
    {
        cout << "Create" << endl;
        this->n = n;
    }
    void Show()
    {
        cout << n << endl;
    }
    ~Test()
    {
    	cout << "Destroy" << endl;
    }
};

void main()
{
    Test(100).Show();
    cout << "-----" << endl;
    const Test& t = Test(200);
    cout << "-----" << endl;
    
    //결과
    //Create
    //100
    //Destroy
    //-----
    //Create
    //-----
    //Destroy
}
  • 참조자는 '임시객체'에 대한 참조가 가능하기 때문에 위와 같이 사용이 가능하다.
  • 이 임시객체는 반환된 뒤 다음 줄로 넘어가면 자동으로 소멸 되지만, '임시객체'에 대한 참조자가 존재하면 바로 소멸되지 않는다.

※ 복사 생성자가 일어나지 않는 예외

SomeClass Func()
{
    return SomeClass
}

void main()
{
    SomeClass sc = Func();
}
  • 위의 코드에서 sc 객체를 생성해서, Func()에서 반환되는 객체를 가지고 대입연산을 진행하는것으로 이해할수도 있지만 위와 같은 상황에서는 객체를 생성하지 않고 반환되는 임시객체에 sc라는 이름을 할당한다고 보면 된다. (객체의 생성 수를 하나 줄여서 효율성을 높이기 위해서이다.)
반응형

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

C++ 함수 오버로딩, Deault 매개변수  (0) 2021.10.24
C++ this 포인터  (0) 2021.10.24
C++ 파일 입출력 ofstream, ifstream  (0) 2021.10.16
C++ 생성자, 복사생성자, 소멸자 (+ const)  (0) 2021.10.15
C++ 동적할당 new, delete  (0) 2021.10.13

+ Recent posts