개체지향 프로그래밍1
OOP 란?
"사람들이 세상을 바라보는 방식" = OOP 의 핵심개념
접근제어자
C++ 의 접근제어자는 private, protected, public 이 있다.
C++ 기본 접근 권한은 private 이다. (Java 의 경우 기본 접근 권한은 패키지 내 접근 가능이다)
보통 다음 코드와 같이 접근제어자 별로 멤버들을 그룹짓는다.
개체 생성
스택
스택 포인터만 변경하면 되기 때문에, 속도가 빠르다.
exe 파일 생성시 별도의 설정이 없다면, 1MB 를 스택으로 사용한다.
힙
기본적으로 힙 메모리를 사용하는 것은 스택에 비해 느리다.
운영체제에 클래스 객체가 생성될 수 있는 메모리 공간을 확인해야 하기 때문에, 느리다..
스택과 힙
C++ 은 스택에 객체를 생성할 수 있다고 했는데, 스택은 무엇일까?
예약 된 로컬 메모리 메모리 공간(작음, 보통 1MB 미만이다) -> 오브젝트 크기가 작다면 개체를 스택에 생성하는 것이 무조건 유리하다. -> 컴파일러 설정을 통해 스택 메모리 크기를 지정할 수 있다. -> 컴파일을 한 .exe 파일 실행할 때, 메모리에 .exe 파일이 올라가고 일반적으로 1MB를 스택으로 사용한다.
함수 호출과 반환이 이 메모리에서 일어난다.
단순히 스택 포인터를 옮긴다.
메모리 할당 및 해제를 할 필요가 없다.
스택에 할당 된 메모리 범위를 벗어나면, 사라진다. (실제로는 사라지기 보다는 나중에 스택 메모리를 사용해야 한다고 하면 덮어쓴다)
변수와 매개변수를 위해 필요한 크기는 컴파일 도중에 알 수 있다. -> 런타임에는 스택 메모리 크기를 판단할 필요가 없다!(힙에 비해 속도 향상)
하지만 스택에 큰 개체를 많이 넣으면
stack overflow 가 발생한다.
성능이 느려질 수 있다.
다음 코드에서 스택의 생성 과정을 살펴보자
스택에 Foo 가 할당된다.
스택에 AddVector 가 할당된다.
AddVector 를 모두 사용하고, 스택 포인터를 Foo 로 이동시킨다.
힙은 무엇일까?
전역 메모리 공간(큼, GB 단위) -> 운영체제가 제공하는 메모리 공간으로 다른 프로그램과 공유될 수도 있다.
비어 있고, 연속적인 메모리 공간을 찾아야만 한다. -> 때문에, 느리다 ..
프로그래머가 직접 할당, 해제를 해주어야 한다.
그렇지 않으면 메모리 누수가 발생한다.
C++ 은 언매니지드 언어이다!
다음 코드에서 힙의 생성 과정을 살펴보자
스택에 Foo 가 할당된다.
스택에 PrintVectors 가 할당된다.
운영체제에 의해서 힙에 Vector 가 할당된다.
힙에 잡혀있던 Vector 가 회수된다.
PrintVectors 를 모두 사용하고, 스택 포인터를 Foo 로 이동시킨다.
개체 배열 생성, 소멸
다음과 같이 Java 와 C++ 에서 개체 배열을 만들 때 메모리 할당 방식이 다르다.
Java
배열의 각 요소에는 Vector 의 레퍼런스(주소)를 저장할 수 있는, 공간이 할당된다.
따로 객체를 생성하기 이전에는 null 값이 할당된다.
초기화와 동시에 10개의 객체를 만드는 공간을 힙에 할당할 수 없다..
C++
C++ 은 직관적이다.
레퍼런스를 넣을 공간을 만드는 것이 아닌, 객체의 값을 넣을 수 있는 공간이 할당된다.
초기화와 동시에 10개의 객체를 만드는 공간을 힙에 할당할 수 있다!
다음과 같이 Java 와 C++ 에서 개체 배열을 만들 때 메모리 소멸 방식이 다르다.
Java
메모리 관리를 개발자가 아닌, GC 에서 해주기 때문에 객체의 사용이 끝났다면(null 로 초기화를 해주면 언젠가는 메모리가 회수 된다) GC 가 메모리를 회수해간다.
하지만 메모리를 즉시 회수해가지 않기 때문에, realtime 서비스에는 적합하지 않다.
C++
delete 지시어를 통해서 반드시 메모리를 회수해주어야 한다.
개체 배열의 메모리 회수 시 [] 를 반드시 넣어 주어야 한다. -> 넣지 않는다면, 배열 크기 만큼에 메모리를 회수하는 것이 아닌, 첫번째 포인터 공간만 회수하게된다.
멤버 변수 초기화
다음과 같이 Java 와 C++ 에서 개체 멤버 변수를 초기화 할 때, 초기화 방식이 다르다.
Java
멤버 변수를 초기화 할 수 있는, 메모리 공간을 가져온다. 이 때는 다른 프로그램에서 사용 했었던 메모리 공간일 수 있기 때문에, 가비지 값이 들어가 있다.
그 후 정수의 경우 기본값인 0으로 초기화 해준다.
C++
멤버 변수를 초기화 할 수 있는, 메모리 공간을 가져온다. 이 때는 다른 프로그램에서 사용 했었던 메모리 공간일 수 있기 때문에, 가비지 값이 들어가있다.
자바와 달리, 기본값으로 초기화하는 것이 아닌, 가비지 값 그대로 가지고 있다. -> 순수하게 성능 때문이다!
new, delete 와 malloc, free 의 차이는 무엇인가?
new delete
메모리 할당과 초기화를 동시에 진행할 수 있다. -> 생성자가 있기 때문이다.
생성자와 소멸자를 호출한다.
할당된 메모리의 크기 변경이 불가능하다.
malloc free
메모리 할당과 초기화를 동시에 진행할 수 없다.
생성자와 소멸자를 호출하지 않는다.
할당된 메모리 크기가 변경이 가능하다.
생성자, 초기화 리스트
생성자에서 대입, 초기화 리스트를 보기에는 같아 보이지만 큰 차이가 있다.
대입 : 개체 생성 후 초기화를 한다.
초기화 리스트 : 개체 생성과 동시에 초기화를 한다.
오류를 범할 수 있는 예는 다음과 같다.
상수(const)나 참조변수(&)는 생성과 동시에 초기화를 해주어야 하기 때문에, 대입의 형태로 사용하게 되면, 오류가 날 수밖에 없다!
생성자와 소멸자
기본 생성자, 컴파일러가 하는 일
기본 생성자는 매개변수를 받지 않는다.
클래스에 생성자가 없으면 컴파일러가 기본 생성자를 자동적으로 만들어 준다.
이렇게 자동적으로 만들어진 생성자는
멤버 변수를 초기화하지 않는다.
하지만 모든 포함된 개체의 생성자를 호출 한다.
생성자 오버로딩(Overrding)
매개 변수를 가지는 생성자로 다음과 같이 사용할 수 있다.
생성자 오버로딩을 하는 순간에 기본 생성자는 컴파일러가 만들어내지 않는다.
소멸자
C++ 에 만 있는 기능이다. -> Java 는 GC 가 있기 때문에, 소멸자가 없다.
동적 할당 된 객체는, delete 시 소멸자가 호출되고,
스택에 할당 된 객체는, 객체가 사용 되지 않을 때 소멸자가 호출 된다.
클래스 안에서 동적 메모리 할당을 하게 되면 어떻게 할까? (string 클래스를 구현해보자)
아래 코드의 문제는 무엇일까? -> 클래스를 스택에 저장하게 되면, 객체가 사용 되지 않을 때 자동적으로 메모리가 반환 되는데, 이때 힙에 메모리가 반환 되지 않고 남아있게 된다. (메모리 누수) -> 때문에, 소멸자를 통해서 힙 메모리를 해제해 주어야 한다!
구조체(Struct) 와 클래스(class)
C++ 에서 구조체와 클래스는 대부분 비슷하다. 다른 차이점은 아래 하나 뿐이다.
기본 접근 권한
struct 는 public
class 는 private
컴퓨터는 이 둘을 구분할까? -> 컴퓨터는 이 둘을 구분하지 못한다..
컴파일러는 이 둘을 구분할까? -> 기본 접근 권한이 다르다는 차이가 있기 때문에, 컴파일러는 이 둘을 구분해야 한다!
주의할 점은 무엇일까?
C++ 에서는 구조체를 클래스처럼 쓸 수 있다.
하지만 절대 그러지 말아라!
구조체는 C 스타일로 사용하자
struct 는 순수하게 데이터뿐이어야 한다!
사용자가 선언한 생성자나 소멸자 X
static 아닌 private / protected 멤버 변수 X
메모리 카피가 가능함!
Last updated