new, delete

Yama·2023년 12월 26일
0

어소트락 수업

목록 보기
26/55

C++ 스타일의 동적 할당과 해제.

  • C스타일
    • malloc, free
  • C++ 스타일
    • new, new [], delete, delete[]

malloc과 free가 해줄 수 없는 부분

  • 클래스가 존재하지않던 C에서 쓰던것이다
  • 클래스는 객체 생성시에 생성자가 호출됬어야됨, 그 객체가 소멸할때는 소멸자가 호출 됫어야됨.
    • 지금까지의 우리가 친 클래스들은 잘 호출이 되었다 클래스 객체를 전역변수나 지역변수로 만들었기 때문에 내가 제작한 특정 클래스의 객체가 존재한다는 사실을 컴파일러가 인지를하고 있기 떄문에 생성자 호출도 넣어주고 함수가 사라질떄 클래스에 구현된 소멸자도 호출됬었다.
  • 동적할당에서는 좀 다르다.
    • 어떤 클래스가 있을것이라는걸 컴파일러가 알수가 없다.
    • 공간의 용도를 모른다.
  • malloc()는 괄호안에 바이트를 두루뭉실하게 적어주면 공간을 주는 것이지 생성자를 호출해준다는걸 컴파일러는 모른다.
    	int* pNew = (int*)malloc(_pArr->MaxCount * sizeof(int));
    • 그래서 공간을 주고 어떤 용도로 쓸것인지 포인터로 지목해서 사용했었다.
    itn size = sizeof(CArr);
    CArr* pArr = (CArr*)malloc(16);
    • malloc(16)적어주고 CArr함수에 16바이트 객체가 들어오니까 저만큼 할당했다고 머리로는 생각하고 주소를 받았다
    • 왜? 생성자 호출안해줬냐? 하는것이랑 따지는것이랑 비슷한것.
    • malloc은 16이라는 숫자밖에 전달받은게 없는데 너가 알아서 눈치껏 생성자를 호출하라는 기능이 없다.
free(pArr);

  • free함수가 이 소멸자를 호출해서 해주는걸 free함수에서 해줄리가 없다리.
  • 동적할당을 하고 내가 만든 클래스 하나를 넣어서 생성자 만들고 쓰다가 해제할떄는 소멸자 호출해서 하는것이 클래스 규칙인데 malloc과 free는 이 규칙과 맞지 않다.
    • 생성자와 소멸자 자동호출 x
  • 해결법 : new, delete

new, delete 키워드

int main()
{

	CArr* pArr1 = new CArr;

	delete pArr1;
   
}
  • new는 동적할당해주는 키워드다.
    • 다음에 자료형을 적는다.(CArr)
  • 템플릿처럼 동작한다 내가 전달해준 자료형에 생성자를 호출 시켜주는것까지 구현해준다
    • 가능한 이유: 생성자가 클래스명이랑 같아서 가능하다.
  • new는 자료형을 전달해주면서 16바이트를 할당해주고 저런 목적으로 써주는구나 그래서 생성자까지 만들어주는구나
    • 가능한 이유: 템플릿처럼 new라는 설계도가 있고 타입(CArr)만 알려주면 사이즈 만큼 돌려주고 이름 같은 생성자를 만들어서.
  • delete도 해제하는 타입까지 호출해준다.
template<typename T>
void MyNew()
{
	int size = sizeof(T);
	T* pNew = (T*)malloc(size);
	pNew->T::T();
}

int main()
{
	MyNew<CArr>();
}
  • new 동작원리(설계도).
  • T들이 CArr로 바꿔서 함수호출된다.

new, delete 정리

  • C++에서는 동적할당을 new, delete 템플릿으로 처리한다.
  • 힙 메모리에 요청할 메모리의 크기를 전달할 자료형 타입의 크기로 처리하고 해당 공간을 알려준 자료형 단위로 쓰인다고 판단하고 해당 자료형이 클래스라면
  • 그 클래스의 생성자 or 소멸자 까지 호출한다.

연속된 공간에서 동적할당 및 해제 하기

CArr* pArr2 = new CArr[10];	// 1

delete pArr2;				// 2
delete[] pArr2;				// 3
  • 1번은 16바이트 짜리 10개가 생긴다.(160바이트)
  • 2번처럼 delete로 하면 맨앞의 거만 해제하고 나머지는 남는다.
  • 3번처럼 delete[]를 해야 배열의 인덱스의 하나하나 소멸자를 호출해서 해제해준다.

malloc -> new , free -> delete 체인지

	m_pData = (int*)malloc(sizeof(int) * m_MaxCount); 
	m_pData = new int[m_MaxCount];
    
    free(m_pData);
   	delete[] m_pData;

클래스 템플릿은 헤더파일에 구현해야 한다.

  • 클래스 템플릿은 헤더랑 cpp을 분할해서 구현하면 안된다.
    • 클래스 템플릿을 요청한 순간에 분할을 해버렸다면 헤더파일에에만 템플릿이 요구되고 cpp파일에는 템플릿이 요구가 안되기 때문에 분할 구현을 하면 링크 오류가 난다.
    • int를 T로 받았다면 헤더만 바뀌고 cpp파일은 안바뀐다.

템플릿이랑 분할구현해놓은거 차이 보기.

CArr.h

#pragma once


class CArr
{
private:
	int*	m_pData;
	int		m_MaxCount;
	int		m_CurCount;

public:
	void push_back(int _Data);

private:
	void Realloc();

public:
	int size() { return m_CurCount; }	
	int capacity() { return m_MaxCount; }
	int at(int _Idx) { return m_pData[_Idx]; }

	int& operator[](int _Idx) { return m_pData[_Idx]; }

public:
	CArr();
	~CArr();
};

CArr.cpp

#include "CArr.h"

#include <iostream>

CArr::CArr()
	: m_pData(nullptr)
	, m_CurCount(0)
	, m_MaxCount(2)
{
	 m_pData = (int*)malloc(sizeof(int) * m_MaxCount);
}

CArr::~CArr()
{
	free(m_pData);
}

void CArr::push_back(int _Data)
{
	if (m_MaxCount <= m_CurCount)
	{
		Realloc();
	}
	m_pData[m_CurCount++] = _Data;
}

void CArr::Realloc()
{
	m_MaxCount *= 2;
	int* pNew = (int*)malloc(sizeof(int) * m_MaxCount);

	for (int i = 0; i < m_CurCount; ++i)
	{
		pNew[i] = m_pData[i];
	}
    
	free(m_pData);

	m_pData = pNew;
}

클래스 템플릿용 CArr.h

#pragma once


// 클래스 템플릿
template<typename T>
class CArr
{
private:
	T*		m_pData;
	int		m_MaxCount;
	int		m_CurCount;

public:
	// 
	void push_back(const T& _Data);			// 1

private:
	void Realloc();

public:
	int size() { return m_CurCount; }
	int capacity() { return m_MaxCount; }
	T at(int _Idx) { return m_pData[_Idx]; }

	T& operator[](int _Idx) { return m_pData[_Idx]; }


public:
	CArr();
	~CArr();
};

template<typename T>
CArr<T>::CArr()
	: m_pData(nullptr)
	, m_CurCount(0)
	, m_MaxCount(2)
{
	m_pData = new T[m_MaxCount];
}

template<typename T>
CArr<T>::~CArr()
{
	delete[] m_pData;
}

template<typename T>
void CArr<T>::push_back(const T& _Data)		// 1
{
	if (m_MaxCount <= m_CurCount)
	{
		Realloc();
	}
	m_pData[m_CurCount++] = _Data;
}

template<typename T>
void CArr<T>::Realloc()
{
	m_MaxCount *= 2;
	T* pNew = new T[m_MaxCount];

	for (int i = 0; i < m_CurCount; ++i)
	{
		pNew[i] = m_pData[i];
	}

	delete[] m_pData;

	m_pData = pNew;
}

const T& _Data

  • 1번 개선사항
  • T가 기본 자료형이 아니라 모든 것을 다 저장할수 있기 떄문에 엄청 큰 자료형을 저장할수 있다.
    • 100바이트짜리를 T로 지정할수도잇다.(한칸한칸이 100)
    • 범용적으로 생각할떄 데이터를 입력할떄 순수하게 그대로 복사하는게아니라 T타입이 지역변수가 되서 너무 커져버린다.
    • 그것을 효율적으로 하기 위해서 그 자체를 참조 받아서 쓰자.&(레퍼런스)
      • 비용이 포인터 쓰는거랑 같다.
    • 원본 수정 방지를 위해서 입력하는 용도로 const사용 했다.

강의 코드

main.cpp

#include <iostream>

#include <vector>
#include "CArr.h"

class CTest
{
public:
	CTest()
	{}
	~CTest()
	{}
};


int main()
{
	int size = sizeof(CTest);
	CTest* pArr = (CTest*)malloc(16);
	free(pArr);

	CTest* pArr1 = new CTest;
	delete pArr1;

	CTest* pArr2 = new CTest[10];
	delete[] pArr2;


	CArr<int> arr;
	arr.push_back(10);
	arr.push_back(20);
	arr.push_back(30);

	int value = 0;
	value = arr.at(0);
	value = arr.at(1);
	value = arr.at(2);

	CArr<float> arr_float;

	arr_float.push_back(1.1f);
	arr_float.push_back(2.2f);
	arr_float.push_back(3.3f);

	float fvalue = 0;
	fvalue = arr_float.at(0);
	fvalue = arr_float.at(1);
	fvalue = arr_float.at(2);


	std::vector<float> vecFloat;
	vecFloat.push_back(1.1f);
	fvalue = vecFloat.at(0);
	fvalue = vecFloat[0];

	vecFloat.size();
	vecFloat.capacity();

	vecFloat.reserve(10);
	vecFloat.resize(10);

	return 0;
}

CArr.h

#pragma once


// 클래스 템플릿
template<typename T>
class CArr
{
private:
	T*		m_pData;
	int		m_MaxCount;
	int		m_CurCount;

public:
	// 
	void push_back(const T& _Data);			// 1

private:
	void Realloc();

public:
	int size() { return m_CurCount; }
	int capacity() { return m_MaxCount; }
	T at(int _Idx) { return m_pData[_Idx]; }

	T& operator[](int _Idx) { return m_pData[_Idx]; }


public:
	CArr();
	~CArr();
};

template<typename T>
CArr<T>::CArr()
	: m_pData(nullptr)
	, m_CurCount(0)
	, m_MaxCount(2)
{
	m_pData = new T[m_MaxCount];
}

template<typename T>
CArr<T>::~CArr()
{
	delete[] m_pData;
}

template<typename T>
void CArr<T>::push_back(const T& _Data)		// 1
{
	if (m_MaxCount <= m_CurCount)
	{
		Realloc();
	}
	m_pData[m_CurCount++] = _Data;
}

template<typename T>
void CArr<T>::Realloc()
{
	m_MaxCount *= 2;
	T* pNew = new T[m_MaxCount];

	for (int i = 0; i < m_CurCount; ++i)
	{
		pNew[i] = m_pData[i];
	}

	delete[] m_pData;

	m_pData = pNew;
}

1차 23.12.26
2차 23.12.27
3차 23.12.28
4차 23.12.29
5차 24.01.02
6차 24.01.03

0개의 댓글