• 카테고리

    질문 & 답변
  • 세부 분야

    프로그래밍 언어

  • 해결 여부

    해결됨

수업시간 코드인데 결과값이 왜 이렇게 나오는지 궁금합니다.

23.03.16 02:02 작성 23.03.16 03:13 수정 조회수 402

1

#include <iostream>
#include "autoptr.h"
//#include "autoptr2.h"
#include "resource.h"
#include "Timer.h"

AutoPtr<Resource> generateResource()  // AutoPtr<Resource> 타입을 리턴하는 함수
{
	// 10000000 의 length를 가진 Resource타입의 멤버를 가지는 AutoPtr 객체 생성
	AutoPtr<Resource> res(new Resource(10000000));

	return res;
}

int main()
{
	using namespace std;
	streambuf* orig_buf = cout.rdbuf();
	// cout.rdbuf(NULL); 화면에 출력되는 메세지들 끄기. 시간 어마어마하게 걸릴테니까 😎

	Timer timer;
	{
		AutoPtr<Resource> main_res; 
		main_res = generateResource(); // ⭐ generateResource() 리턴값은 R-value 
	}
	cout.rdbuf(orig_buf);
	//cout << timer.elapsed() << endl;
	timer.elapsed();//실행시간 재서 출력
}

메인.cpp

#pragma once
#include <iostream>
using namespace std;

template<typename T>
class AutoPtr
{
public:
	T* m_ptr;

public:
	AutoPtr(T* ptr = nullptr)
		:m_ptr(ptr)
	{
		cout << "AutoPtr default constructor" << endl;
	}
	~AutoPtr()
	{
		cout << "AutoPtr destructor" << endl;

		if (m_ptr != nullptr) delete m_ptr;
	}
	AutoPtr(const AutoPtr& a) // 💎복사 생성자💎
	{
		cout << "AutoPtr copy constructor" << endl;

		// deep copy
		m_ptr = new T;  // T가 Resource 타입으로 들어오면 m_ptr은 Resource 타입의 포인터
		*m_ptr = *a.m_ptr;  // ⭐Resource의 '대입 연산자 오버로딩 호출 , deep copy
	}
	AutoPtr& operator = (const AutoPtr& a) //L-value 레퍼런스 , 💎대입 연산자 오버로딩💎
	{
		cout << "AutoPtr copy assignment" << endl;

		if (&a == this)
			return *this;

		if (m_ptr != nullptr) delete m_ptr;

		// deep copy
		m_ptr = new T;  // 새로운 빈 공간 할당 받기. T가 Resource 타입으로 들어오면 m_ptr은 Resource 타입의 포인터
		*m_ptr = *a.m_ptr; // ⭐Resource의 '대입 연산자 오버로딩' 호출 , deep copy

		return *this;
	}
	T& operator *() const { return *m_ptr; }
	T* operator ->() const { return m_ptr; }
	bool inNull() const { return m_ptr == nullptr; }
};

AutoPtr 헤더파일

#pragma once
#include <iostream>
using namespace std;

class Resource
{
public:
	int* m_data = nullptr;
	unsigned m_length = 0;

public:
	Resource() // 기본 생성자
	{
		cout << "Resource constructed" << endl;
	}

	Resource(unsigned length) // 일반 매개변수 1개 생성자
	{
		cout << "Resource length constructed" << endl;
		this->m_data = new int[length];
		this->m_length = length;
	}

	Resource(const Resource& res) // 💎복사 생성자💎 
	{
		cout << "Resource copy constructed" << endl;

		Resource(res.m_length);

		for (unsigned i = 0; i < m_length; ++i)  // 내용물을 전부 깊은 복사 (시간이 꽤 걸림)
			m_data[i] = res.m_data[i];
	}

	~Resource()  // 소멸자
	{
		cout << "Resource destroyed" << endl;
	}

	Resource& operator = (Resource& res)  // 💎대입 연산자 오버로딩💎
	{
		cout << "Resource copy assignment" << endl;

		if (&res == this) return *this; // 대입하려는게 자기 자신이면 아무것도 안함

		if (this->m_data != nullptr) delete[] m_data; // 1. 내 자신의 m_data 비워주기

		m_length = res.m_length; // 2. 대입으로 넘겨받은 res의 length 로 내 length 갱신

		m_data = new int[m_length]; // 3. 비워진 내 자신의 m_data에 새로운 공간 할당받기
		for (unsigned i = 0; i < m_length; ++i) // 4. m_data내용물 넣기.
			m_data[i] = res.m_data[i]; //  대입으로 넘겨받은 res의 m_data 내용물들을 **내 m_data**에 깊은 복사

		return *this;
	}
};

Resource헤더파일

 

결과값이

AutoPtr default constructor

Resource length constructed

AutoPtr default constructor

AutoPtr copy assignment

Resource constructed

Resource copy assignment

AutoPtr destructor

Resource destroyed

AutoPtr destructor

Resource destroyed

 

 

이렇게 나왔습니다.

여기서 궁금한점은 소멸자 출력문구 순서인데요

디버깅을 해봤는데

Timer timer;

{

AutoPtr<Resource> main_res;

main_res = generateResource();

}

이 지역범위를 벗어나고 소멸자 호출을 시작하는데 정확히 main_res = generateResource(); 이부분에서

AutoPtr destructor

Resource destroyed

AutoPtr destructor

Resource destroyed

이 4번의 소멸자가 호출됩니다.

 

그에 대한 설명을 나름 추리해봤는데 제 설명이 맞는지 확인해주시고 혹시 틀렸다면 정확히 어떤점에서 틀렸고 어떠한 근거로 저 소멸자호출문구가 출력 되는건지 알려주실 수 있나요?

 

 

저의 설명

==================================

AutoPtr destructor >>> generateResource() 함수의 리턴 값인 res를 블럭이 끝나면서 소멸자로 할당값을 해제해주는 과정에서 소멸자 호출

 

Resource destroyed >>> 소멸자 호출 문구 출력 이후 if (m_ptr != nullptr) delete m_ptr; 구문 실행과정에서 m_ptr이 resource타입이니까 삭제시에 resource 소멸자 호출

 

AutoPtr destructor >>> main_res 역시 해제하기 위해 autoptr의 소멸자 호출

 

Resource destroyed >>>소멸자 호출 문구 출력 이후 if (m_ptr != nullptr) delete m_ptr; 구문 실행과정에서m_ptr이 resource타입이니까 삭제시에 resource 소멸자 호출

 

 

답변 1

답변을 작성해보세요.

3

Soobak님의 프로필

Soobak

2023.03.16

안녕하세요, 답변 도우미 Soobak 입니다. 😀

설명해주신 부분 꼼꼼히 읽어보았습니다. 전반적으로 옳게 학습하신 것 같습니다만,
제가 질문자님의 설명을 잘 이해하지 못했을 수도 있다는 혹시나 하는 마음에 추가적으로 자세히 설명드려봅니다.😀

먼저, main.cpp 파일의 아래 코드 부분에서 출력이 이루어지므로, 해당 부분만 발췌하여 설명드리겠습니다.

Timer timer;

{
    AutoPtr<Resource> main_res;
    main_res = generateResource();
}
  1. main_res 객체가 생성됩니다. 이 때 AutoPtr의 기본 생성자가 호출되고, "AutoPtr default constructor"가 출력됩니다.

  2. generateResource() 함수가 호출되어, 함수 내부에서 새로운 AutoPtr<Resource> 객체가 생성되고 Resource가 초기화됩니다. 이 때 "Resource length constructed""AutoPtr default constructor"가 출력됩니다.

  3. generateResource() 함수가 AutoPtr<Resource> 객체를 반환합니다. 이 반환된 객체는 임시 객체로, main_res에 대입됩니다. 대입 과정에서 AutoPtr의 대입 연산자 오버로딩이 호출되어 "AutoPtr copy assignment"이 출력되고, Resource의 대입 연산자 오버로딩도 호출되어 "Resource copy assignment"이 출력됩니다.

  4. generateResource()에서 반환된 임시 객체가 main_res 에 할당되는 순간 생명 주기가 끝나므로, 이 임시 객체의 소멸자가 호출됩니다. 이 때 "AutoPtr destructor""Resource destroyed"가 출력됩니다.

  5. 이제 중괄호가 끝나므로, main_res 객체의 생명 주기가 끝나게 되고, AutoPtr 클래스의 소멸자가 호출됩니다. 이 때 "AutoPtr destructor""Resource destroyed"가 출력됩니다.

어떤 함수가 값을 반환함에 있어서, 값의 반환은 임시 데이터를 생성한 후 그 임시 데이터를 대입할 변수에 복사하는 과정으로 이루어집니다. 강의 7.5 다양한 반환 값들(값, 참조, 주소, 구조체, 튜플)2:07 부분 참고해보시면 좋을 것 같습니다.

따라서,

main_res = generateResource();

이 코드가 실행되면, generateResource() 함수가 호출되어 AutoPtr<Resource> 타입의 임시 객체가 반환됩니다. 이후 이 임시 객체는 main_res 변수에 할당되면서, 임시 객체의 생명 주기가 끝나게 됩니다. 이때 임시 객체의 소멸자가 호출되어 "AutoPtr destructor" "Resource destroyed" 출력되는 것입니다.

 

꼼꼼하게 학습하시는 부분이 멋있으십니다. 👍

또 궁금하신 점이 있으시다면, 언제든지 편하게 질문 남겨주시면 감사하겠습니다. 화이팅!

 

seungmin38님의 프로필

seungmin38

질문자

2023.03.16

꼼꼼하게 답변해주셔서 정확히 이해했습니다. 감사합니다 정말요