티스토리 뷰

개체 생성

class Vector
{
};

스택

Vector a; // 스택 메모리에 만들기
  • 스택
    • 예약된 로컬 메모리 공간 (작다, 일반적으로 1MB 이하)
    • 함수 호출과 반환이 이 메모리에서 일어남
    • 단순히 스택 포인터를 옮기는 것
      • 메모리를 할당 및 해제할 필요 없음
      • 스택에 할당된 메모리는 범위(scope)를 벗어나면 사라짐
      • 변수와 매개변수를 위해, 필요한 크기는 컴파일 도중에 알 수 있음
    • 하지만 스택에 큰 개체를 많이 넣으면
      • 스택 오버플로우(overflow)가 발생할 수 있음
      • 👁️ 성능이 느려질 수도 있음
        • OS 에서 메모리 정책을 생각해보자
          • 당장 쓰지 않는 부분은 swap file 로 남겨둠
            • file 페이징, 메모리 페이징 처리
          • 즉, 필요할때 갖고오는 것
        • 따라서, 필요한 시점에 메모리에 load될때 느려질 수 있음

Vector* b = new Vector(); // 힙 메모리에 만들기
    • 전역 메모리 공간 (큼, ~GBs)
    • 비어있고 연속된 메모리 블록을 찾아야함
    • 프로그래머가 메모리를 직접 할당 및 해제해야함
      • 그렇지 않으면, 메모리 누수 발생

힙 vs 스택

  • 스택을 쓸 수 있으면 무조건 스택을 쓰는게 좋다.
  • 힙을 써야하는 경우는
    1. 한 함수 스코프 안에서 안끝나고, 여기저기 패스해야하는 경우
    2. 함수에서 반환값으로 오브젝트를 그대로 반환하면 그 값이 복사되면서 호출자한테까지 전달되는데 그 오브젝트가 너무 크다. 그런 경우 차라리 힙에 오브젝트를 만들고 주소 하나를 반환하는게 빠름 (메모리 복사 많이 하는 것보다 빠르다 - 이 경우 오브젝트 크기하고 상관이 있음을 알 수 있음)

개체 배열(array)

Vector* list = new Vector[10];

Quiz. 무엇을 만들까? Vector의 포인터 vs Vector ?

A. 진짜 Vector 를 만든다

  • 생성자로 값을 채운 실제 개체가 잡힌다
  • 기억해두자
    • new Vector[10]; 에서 포인터 표시가 없음

한번에 보기

// 10개의 벡터 개체를 힙에 만듦
Vector* list = new Vector[10];

// 10개의 (벡터 개체의)포인터를 힙에 만듦
Vector** list = new Vector*[10];

개체 소멸

Vector* a = new Vector;
Vector* list = new Vector[10];
// ...

delete a; // 메모리가 즉시 해제됨

delete[] list; // [] 를 반드시 넣어야함

🔥 메모리 누수 방지

  • new 를 사용했다면 꼭 delete 하자
  • 안한다면, 프로그램 종료까지 메모리를 다른곳에서 쓸수가 없다 ㅜㅜ

멤버 변수 초기화

class Vector
{
private:
	int mX;
	int mY;
};

Vector a;
Vector* b = new Vector();

스택이나 힙 어디에 생기던,

  • 값이 초기화 되지 않음
  • 전에 메모리 공간에 저장되어있던 값을 사용함
  • Java 등에서 초기화되는건, 안전은 하겠지만, 성능을 희생하는 것임

만약, 0으로 초기화한다면… (C 의사코드)

Vector a = new Vector();

// C 의사코드
void* ptr = malloc(sizeof(Vector));
memset(ptr, 0, sizeof(Vector)); // 👈
a = (Vector*)ptr;

new / delete vs malloc() / free()

내 생각 정리

  • malloc() / free()
    • 전통적인, 힙메모리 할당기 이다
    • malloc 으로 원하는 크기를 할당해오고, 그 주소를 반환받는다
    • 할당해온 메모리의 주소를 free 로 반환한다
    • 개체를 생각하지 말고, 딱 함수의 개념으로 이해하자
  • new / delete
    • 개체의 생성과 소멸에 관여되는 키워드, 언어차원에서 개체 관리를 위해 지원
    • 생성자/소멸자를 호출하여 그 로직을 실행시키는 추가 기능(개체 관리)이 있다고 크게 정리하면 어떨까? ㅎㅎ

초기화 리스트(Initializer List)

초기화 리스트

class Vector
{
public:
	Vector()
		: mX(0)
		, mY(0)
	{
	}
private:
	int mX;
	int mY;
};
  • 개체 생성 시, 초기화 되는 것임. 대입이 아님
  • const, 레퍼런스(참조) 변수를 초기화 가능하다

참고로, 대입

class Vector
{
public:
	Vector()
	{
		mX = 0;
		mY = 0;
	}
private:
	int mX;
	int mY;
};

헤더파일과 cpp파일 분리해서 작성

  • 헤더파일
class Vector
{
public:
	Vector();
	Vector(int x, int y);
private:
	int mX;
	int mY;
};
  • cpp파일
Vector::Vector()
	: mX(0)
	, mY(0)
{
}

Vector::Vector(int x, int y)
	: mX(0)
	, mY(0)
{
}

컴파일러와 생성자

기본 생성자

  • 기본 생성자는 매개변수를 받지 않음
  • 클래스에 생성자가 없으면 컴파일러가 기본 생성자를 자동적으로 만들어줌
    • 이렇게 자동적으로 만들어진 생성자는, 멤버 변수를 초기화하지 않음
    • 구현체 코드가 아래와 같이 생성됨
Vector::Vector()
{
}

컴파일러와 생성자

  1. 클래스에서 생성자가 없다면
    • 기본 생성자를 만들어줌
  2. 클래스에서 생성자가 있다면
    • 아무 작업 하지 않음 (기본생성자를 만들어주지 않음)

생성자 오버로딩(overloading)

  • 여러개의 생성자를 만들 수 있음
    • 같은 이름
    • 인자의 개수나 자료형은 다름

매개변수 목록이 일치하는 생성자를 호출한다.

  • 기본 생성자 예시
Vector() : mX(0), mY(0) {}

// 기본 생성자 호출
Vector a;
Vector a();
  • 매개 변수를 가지는 생성자 예시
Vector(int x, int y)
	: mX(x)
	, mY(y)
{
}

Vector a(1, 3);

생성자 (Constructor)

언제 호출될까? (생성할때)

  1. 스택에 생성할때
  2. 힙에 생성할때 (new)

소멸자 (Destructor)

언제 호출될까? (지워질때)

  1. 스택 범위가 지워질때
  2. new 로 만든 개체를 delete로 지울때
// vector.h
class Vector
{
public:
	~Vector();  // 소멸자
	
private:
	int mX;
	int mY;
};

// vector.cpp
Vector::~Vector()
{
}

소멸자

  • 개체가 지워질 때 호출됨
  • 가상 소멸자(동적 바인딩)
  • C++ 는 언매니지드 언어
    • 👁️ C++ 클래스는 그 안에서 동적으로 메모리를 할당할 수도 있음
    • 그런 경우 필히 소멸자에서 메모리를 직접 해제해 줘야한다

클래스 안에서의 동적 메모리 할당하는 경우 (소멸자에서 메모리 직접 해제 필요)

// MyString.h
class MyString
{
public:
	MyString();
private:
	char* mChars;
	int mLength;
	int mCapacity;
};

// MyString.cpp
MyString::MyString()
	: mLength(0)
	, mCapacity(15)
{
	mChars = new char[mCapacity + 1]; // 클래스 안에서의 동적 메모리 할당
}
  • 🔥 메모리 해제 !!!!
// MyString.h
class MyString
{
public:
	MyString();
	~MyString();
private:
	char* mChars;
	int mLength;
	int mCapacity;
};

// MyString.cpp
MyString::MyString()
	: mLength(0)
	, mCapacity(15)
{
	mChars = new char[mCapacity + 1]; // 클래스 안에서의 동적 메모리 할당
}

MyString::~MyString()
{
	delete[] mChars;
	// mCapacity = 0;
	// mChars = NULL;
}

Const 멤버함수

// vector.h
class Vector
{
public:
	void SetX(int x);
	void SetY(int y);
	int GetX() const; // 👈
	int GetY() const; // 👈
	void Add(const Vector& other);	
	
private:
	int mX;
	int mY;
};
  • const 메서드 : 해당 개체 안의 어떠한 것도 바꾸지 않음
void Add(const Vector& other) const
{
	mX = mX + other.mX; // 컴파일 에러
	mY = mY + other.mY; // 컴파일 에러
}	
  • 🔥 🤙 기본적으로는 const 함수로 작성하자
    • 제한적으로 작성하다가, 정말 필요하면 제거하자

구조체(struct) vs 클래스(class) in C++

기본 접근 권한

struct Vector
{
	int X;
	int Y;
};

class Vector
{
	int mX;
	int mY;
};
  • struct
    • public
  • class
    • private
struct Vector
{
	int X; // public 멤버변수
	int Y; // public 멤버변수
};

class Vector
{
	int mX; // private 멤버변수
	int mY; // private 멤버변수
}; // 안좋은 코딩 스타일. private: 를 명시해주자

🔥 Q. 컴퓨터는 이 둘을 구분할까?

A. 당연하게도 아니요. 컴퓨터는 모름. 단지 언어(컴파일러)단에서 구현한 개념임.

구조체에 관한 코딩표준

  • C++에서는 구조체를 클래스처럼 쓸 수 있다
    • 하지만 절대 그러지 말 것
    • 🔥 구조체는 C 스타일로 쓰자
  • 🔥 struct 는 순수하게 데이터뿐이여야함 (Plain Old Data, POD)
    • 사용자가 선언한 생성자나 소멸자 X
    • static 아닌 private/protected 멤버 변수 X
    • 가상 함수 X
    • 메모리 카피가 가능함
      • memcpy() 를 사용하여 struct를 char[]로, 혹은 반대로 복사할 수 있음
    • 👁️ 까탈스럽게 가면, 인디렉션 자체를 하지않도록 하자(파일 저장 가능하게)

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

[C++] low level 개체지향 프로그래밍 3  (1) 2024.10.06
[C++] low level 개체지향 프로그래밍 2  (1) 2024.10.06
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함