생성자 추가 내용과 해더 파일을 이용해 클래스 분리하는것에 대한 수업을 진행하였다.
1. class 초기화
#include <iostream>
class C_CIRCLE
{
private:
float m_fPi;
public:
C_CIRCLE();
float getData();
};
int main()
{
C_CIRCLE c;
printf("%f\n", c.getData());
}
C_CIRCLE::C_CIRCLE() : m_fPi{}
{
m_fPi = 3.141591f;
}
float C_CIRCLE::getData()
{
return m_fPi;
}
1.1. 생성자와 초기화 리스트
- C_CIRCLE::C_CIRCLE() : m_fPi{}:
- 이 부분은 초기화 리스트를 통해 m_fPi를 기본값 {}로 초기화한다. {}는 0.0f와 같은 역할을 하여 m_fPi를 0으로 초기화를 수행한다.
- 초기화 리스트를 사용하면 객체의 생성과 동시에 멤버 변수를 초기화할 수 있다.
- 초기화 리스트의 사용은 생성자의 실행 전에 멤버 변수를 초기화하기 때문에 메모리 할당과 초기화가 효율적으로 이루어 진다.
1.2. 생성자 내부의 대입 연산자 사용
- m_fPi = 3.141591f;:
- 초기화 리스트 이후에, 생성자 내부에서 m_fPi에 3.141591f 값을 대입하고 있다..
- 이 작업은 초기화가 아니라 대입이다.
즉, 초기화 리스트로 0.0f로 초기화된 m_fPi에 대해, 생성자가 실행되는 시점에서 새로운 값을 할당하는 것이다. - 초기화와 대입의 차이점 :
- 초기화: 메모리가 할당되는 순간 값을 설정하는 과정 (초기화 리스트 사용).
- 대입: 메모리가 할당된 이후, 이미 초기화된 값에 새 값을 넣는 과정.
1.3. 객체 생성과 멤버 함수 호출
- C_CIRCLE c;:
- main 함수에서 C_CIRCLE 클래스의 객체 c를 생성한다.
- 이 과정에서 C_CIRCLE의 생성자가 호출되며, m_fPi가 초기화 리스트를 통해 먼저 0으로 초기화되고, 그 후에 3.141591f가 대입된다.
- c.getData():
- getData 함수는 m_fPi의 값을 반환한다. 따라서 main 함수에서 printf를 사용해 m_fPi의 값을 출력하면 3.141591이 출력이 된다.
1.4 요약
- 초기화 리스트 사용: 생성자가 호출될 때 m_fPi를 초기화 리스트를 통해 먼저 0으로 초기화한 후, 생성자 본문에서 다시 3.141591f로 값을 설정한다.
- 대입과 초기화의 차이: 초기화 리스트는 객체의 생성과 동시에 값을 설정하지만, 생성자 내부에서의 대입은 객체가 생성된 후 이미 할당된 메모리의 값을 변경하는 것이다.
- 객체 생성과 멤버 함수 호출: 객체 c가 생성될 때 생성자가 실행되고, 이후 getData()를 통해 m_fPi의 값을 확인할 수 있습니다. 결과적으로 printf로 3.141591이 출력이 된다.
1.5 추가 설명
- 초기화 리스트의 필요성:
- 멤버 변수를 선언과 동시에 값을 초기화하려면 초기화 리스트를 사용해야 한다. 메모리가 할당되는 시점에서 동시에 초기화가 이루어지기 때문이다.
- 이는 객체가 생성되는 동시에 원하는 값을 세팅하는 유일한 방법이다.
- 초기화 방식의 두 가지 방법:
- 생성자 내부에서 초기화.
- 초기화 리스트를 사용하여 초기화. 초기화 리스트는 생성과 동시에 값을 세팅하겠다는 의미이다.
- 생성자에서 값을 강제로 요청하는 이유:
- 생성자가 값을 강제로 요청하면, 해당 객체는 반드시 특정한 초기 상태를 가지게 된다. 이는 객체의 일관성을 보장하지만, 복잡한 모듈에서는 이러한 강제 초기화가 불편할 수 있다.
- 초기화 리스트의 장점과 단점:
- 장점: 생성 시점 이후 특정 값이 반드시 설정되어 있어야 함을 보장할 수 있다.
- 단점: 모듈이 복잡해질수록 초기화 순서가 우선순위를 가질 수 있어, 작업이 제한적일 수 있다.
- 늦은 초기화의 개념:
- 생성 시점에는 객체가 생성되지만, 실제로 값이 설정되는 것은 나중으로 미루는 방식이다. 이는 객체의 생성과 조립 단계를 분리하여 초기화 순서에 대한 제약을 줄인다.
- 늦은 초기화의 필요성:
- 복잡한 모듈에서, 객체 생성 시점에 필요한 값을 미리 요청하는 대신, 초기화 작업을 따로 처리하여 유연하게 조립할 수 있다. 이는 사용 준비와 초기화를 분리하여, 특정 값이 준비된 후에야 해당 객체를 사용할 수 있도록 만든다.
- 초기화와 청소의 차이:
- 청소: 변수에 임의의 값을 초기화하여(대개 0) 사용 전에 안전한 상태를 만든다.
- 초기화: 변수에 사용 가능한 값을 할당하여 객체가 바로 사용될 수 있도록 준비하는 것. 예를 들어, int형 변수는 청소만으로도 사용이 가능하지만, 다른 타입은 청소와 초기화의 개념이 분리될 수 있다.
2. 생성자와 explicit 키워드
- 생성자는 함수의 원칙을 따른다:
- 생성자는 객체를 초기화할 때 호출되는 특별한 함수로, 함수의 구조와 유사하게 인수를 받는다.
- C++에서 모든 자료형은 클래스와 같은 개념으로 다루어지며, 객체를 생성할 때 생성자의 문법에 따라 초기화가 이루어집니다.
- 인수가 하나인 생성자와 암시적 변환:
- 인수가 하나인 생성자는 암시적 변환이 발생할 수 있습니다. 예를 들어, C_DATA라는 클래스가 있고, C_DATA(int)라는 생성자가 있을 때 C_DATA c1 = 10;처럼 =를 사용하여 객체를 생성하면, int 값 10이 C_DATA 타입으로 자동 변환되어 생성자가 호출됩니다.
- 이러한 암시적 변환은 가독성을 해칠 수 있으며, 코드에서 의도하지 않은 변환을 발생시킬 수 있습니다.
- explicit 키워드 사용:
- 인수가 하나인 생성자 앞에 explicit 키워드를 붙이면, 암시적 변환을 막을 수 있습니다.
- explicit 키워드를 사용하면, C_DATA c1 = 10;와 같은 코드가 허용되지 않고, C_DATA c1(10);처럼 명시적으로 생성자를 호출해야 합니다.
- 즉, explicit 키워드는 객체 생성 시 암시적 변환을 방지하여 코드의 명확성을 높이는 역할을 합니다.
- {} 초기화의 사용 제한:
- {} 초기화는 강제 초기화라고도 불리며, 특정한 경우에만 사용하는 것이 좋습니다:
- 배열 초기화: 배열 데이터를 초기화할 때 {}를 사용하여 배열의 요소들을 설정할 수 있습니다.
- 구조체 초기화: 구조체의 멤버들을 초기화할 때도 {}를 사용할 수 있습니다.
- 그러나 구조체 초기화에서 {} 사용을 지양하는 이유는, 구조체 멤버의 순서나 멤버 맞춤(alignment) 때문에 예상치 못한 값이 설정될 수 있기 때문입니다.
- 배열의 경우, 멤버 변수로 사용하는 배열은 직접 초기화가 불가능하지만, 지역 변수 배열을 초기화할 때 {}를 사용할 수 있습니다.
- {} 초기화는 강제 초기화라고도 불리며, 특정한 경우에만 사용하는 것이 좋습니다:
인수가 두 개인 생성자와 explicit
- 인수가 두 개 이상인 경우:
- 인수가 두 개 이상인 생성자에서는 암시적 변환이 기본적으로 발생하지 않습니다. 예를 들어, C_DATA(int, int) 생성자가 있을 때 C_DATA c1 = 10, 20;와 같은 코드는 컴파일 오류가 발생합니다.
- 따라서 explicit 키워드를 붙여도 특별히 효과가 없습니다. = 연산자를 사용하는 초기화 자체가 허용되지 않기 때문입니다.
- {} 사용의 제한점:
- {} 초기화는 강제로 값을 설정하기 때문에, 인수가 두 개 이상인 생성자에서도 사용할 수는 있지만, 명확하지 않은 코드가 될 수 있습니다.
- 복잡한 데이터 구조를 다룰 때는, {} 초기화를 지양하고 명시적으로 생성자를 호출하여 초기화하는 것이 더 안전하고 가독성이 좋습니다.
C++ 헤더 파일과 클래스 정의
- 헤더 파일 생성과 #pragma once 사용:
- 헤더 파일(.h): 클래스 정의, 함수 프로토타입, 전역 변수, 전방 선언 등을 포함하는 파일입니다.
- 헤더 파일의 최상단에 #pragma once를 추가하면 해당 파일이 한 번만 포함되도록 전처리기에 알려줍니다.
- 이 지시어는 중복 포함 방지를 위해 사용되며, 코드의 안전성과 컴파일 시간을 줄이는 데 도움을 줍니다. 예를 들어, #pragma once가 없는 경우 헤더 파일이 여러 번 포함될 수 있어 오류가 발생할 수 있습니다.
- 클래스, 헤더 파일, CPP 파일 이름 일치:
- 클래스 정의를 분리할 때, 클래스의 이름과 헤더 파일(.h) 및 구현 파일(.cpp)의 이름을 일치시키는 것이 원칙입니다.
- 예를 들어, C_CIRCLE 클래스가 있다면 C_CIRCLE.h와 C_CIRCLE.cpp로 파일을 나눕니다.
- 이렇게 하면 코드의 구조가 명확해지고, 유지보수가 용이해지며, 동일한 클래스 관련 파일들을 쉽게 찾을 수 있습니다.
- 전처리기 지시어 #include 사용:
- #include " "와 #include < >의 차이:
- #include "파일명": 프로젝트 내에 있는 파일을 포함할 때 사용합니다. 예를 들어, 사용자가 작성한 헤더 파일을 포함할 때 사용합니다.
- #include <파일명>: 표준 라이브러리나 외부 라이브러리에서 파일을 포함할 때 사용합니다. 예를 들어, <iostream>이나 <vector> 같은 표준 헤더 파일을 포함할 때 사용합니다.
- #include를 사용할 때는 < >로 작성된 표준 라이브러리 포함문이 "로 작성된 프로젝트 파일 포함문보다 위에 오는 것이 좋습니다. 그렇지 않으면 헤더 파일 중첩 문제나 불필요한 충돌이 발생할 수 있습니다.
- #include " "와 #include < >의 차이:
단축키 사용
- 파일 간 이동 단축키:
- Ctrl + Tab: 여러 개의 열린 파일을 순환하며 이동할 수 있는 단축키입니다. 현재 열려 있는 파일을 빠르게 전환할 때 유용합니다.
- Ctrl + K + O: 같은 이름을 가진 헤더(.h)와 구현 파일(.cpp) 간에 이동할 때 사용합니다. 클래스 이름과 파일 이름이 같아야 이 단축키가 제대로 동작합니다.
- 파일 이름 변경 단축키:
- F2: 현재 선택된 파일이나 폴더의 이름을 변경할 수 있는 단축키입니다. 헤더 파일과 CPP 파일 이름을 일치시킬 때 유용합니다.
요약
- 헤더 파일 생성 시 #pragma once를 사용하여 중복 포함을 방지합니다.
- 클래스의 이름과 헤더/CPP 파일의 이름을 일치시켜 코드 구조를 명확하게 유지합니다.
- #include의 " "와 < >의 차이를 알고, 올바른 순서로 포함하여 충돌을 방지합니다.
- 유용한 단축키를 사용하여 코딩 속도를 높이고, 파일 간 전환을 쉽게 할 수 있습니다.
'C++' 카테고리의 다른 글
[강의] 10월 25일 수업정리 (0) | 2024.10.27 |
---|---|
[강의] 10월 24일 수업정리 (0) | 2024.10.25 |
[강의] 10월 22일 수업정리 (1) | 2024.10.23 |
[강의] 10월 18일 수업정리 (1) | 2024.10.20 |
[강의] 10월 17일 수업정리 (1) | 2024.10.18 |