c++는 현재 플랫폼에서 지원하는 숫자의 경계값을 알아내는 표준 방법을 제공한다.
numeric_limits<T>::max();
numeric_limits<T>::min();
numeric_limits<T>::lowest();
max는 최댓값
min과 lowest는 최솟값을 나타낸다.
정수에서는 min 과 lowest가 같은 값을 출력하지만
부동소수점수에서는 min은 표현가능한 가장 작은 양의값
lowest는 표현 가능한 가장 작은 음수 이다.
캐스트: 실행중에 변수 타입을 바꾸는 것
명시적 변환
flaot myFloat {3.14f};
int i1 { (int)myFloat }; //C에서 주로 쓰던 방법
int i2 { int(myfloat) }; //거의 쓰이지 않음
int i3 { static_cast<int>(myfloat) }; // 권장됨
표현범위가 좁은 변수에 넓은 타입의 값을 대입하려 하면 데이터가 손실되며 강제 형변환이 일어난다.
부동소수점수의 특별한 값
+/- 무한: 양의 무한, 음의 무한
NaN: 숫자가 아님. 0/0과 같이 수학적으로 정의되지 않은 값
#include<cmath>
std::isnan() // NaN인지 확인
std:isinf() // 무한인지 확인
연산자
이항 연산자
단항 연산자
삼항 연산자
연산자 우선순위 주의. 헷갈릴꺼 같으면 괄호로 묶자
열거타입
숫자를 나열하는 방식과 범위를 마음대로 정의해서 변수를 선언하는 데 활용할 수 있다.
강타입 열거타입
enum class PieceType { King, Queen, Rook, Pawn }; // 0, 1, 2, 3
PieceType piece { PieceType::King };
enum 타입을 구성하는 멤버는 내부적으로 정숫값으로 표현된다.
자동으로 정수로 변환되지 않는다는 것을 주의.
서로 다른 열거 타입에 동일한 이름의 멤버가 존재할 수 있다.
using PieceType::King;
PieceType piece { King };
using을 이용하면 열것값을 길게 풀어쓰지 않아도 된다.
다만 남용하면 enum 스코프가 곂쳐 문제가 생길 수 있다.
# 예전 방식의 열거 타입( enum PieceType 같은)은 스코프가 자동으로 확장되므로 enum 간의 같은 이름의 멤버를 사용할 수 없다. 타입에 안전하지 않다.
구조체
타입을 한 개 이상 묶어서 새로운 타입으로 정의할 수 있다.
모듈 인터페이스 파일 .cppm
모듈 선언문 <- 모듈을 새로 정의한다는 뜻
명시적으로 export 해야 함 (다른 곳에서 이 모듈을 임포트 할 수 있도록 드러냄)
ㄴ 그 대상 앞에 export 키워드를 붙인다.
employee.ixx
export module employee;
export struct Employee
{
char firstInitial;
char lastInitial;
int employeeNumber;
int salary;
};
책에서는 cppm이라고 소개했지만 모듈 확장자에 대한 표준이 없는지라 VS에서는 ixx라는 확장자를 사용하고 있었다.
import <iostream>;
import <format>;
import employee;
using namespace std;
int main()
{
Employee anEmployee;
anEmployee.firstInitial = 'J';
anEmployee.lastInitial = 'D';
anEmployee.employeeNumber = 42;
anEmployee.salary = 80000;
cout << format("Employee: {} {}", anEmployee.firstInitial,
anEmployee.lastInitial) << endl;
cout << format("Number: {}", anEmployee.employeeNumber) << endl;
cout << format("Salary: ${}", anEmployee.salary) << endl;
}
import로 참조 가능
조건문
if / else
else if
if 문 안에 초기자를 넣을 수 있다.
if(<초기자>; <조건문>){
//if의 본문
}else if(<else if의 조건문>){
//else if의 본문
}
...
if (Employee employee { getEmployee() }; employee.salary > 1000)) { ... }
for문에서 int i 치고 시작하는 것 처럼 초기자를 명시하면 if - else if - else 내에서만 쓸 수 있는 변수를 선언하는 것이 가능하다.
switch문
switch문에 지정할 수 있는 표현식은 결괏값이 반드시 정수 타입이거나,
정수 타입으로 변환할 수 있는 타입이거나, 열거 타입이거나,
강타입 열거타입이여야 하며, 상수와 비교할 수 있어야 한다.
swtich 문에서 break문 없이 바로 다음 case도 실행되게 하는것을 폴스루 (흘려보내기) 라고 한다.
보통 까먹고 break를 빠뜨리므로 주의
case 내부에 [[fallthrough]] 어트리뷰트를 명시해주면 컴파일러가 경고를 띄우지 않는다.
switch문도 if 문처럼 초기자를 지정할 수 있다.
조건 연산자는 C++에서 인수 세개를 받는 유일한 삼항 연산자이다.
[조건]? [동작 1]:[동작 2]
논리연산자
c++는 논리 표현식을 평가할 때 단락 논리(축약 논리)를 사용한다.
표현식을 평가하는 도중에 최종 결과가 나오면 나머지 부분은 평가하지 않는다.
예를들어 or로 묶인 표현식이 있으면 가장 처음 논리만 true면 볼 것도 없이 true이기 때문에 뒤의 표현식을 실행하지 않는다.
잘 사용하면 최적화에 유용하지만, 뒤의 표현식이 스킵되기 때문에 주의해야 한다.
3방향 비교연산자 <=> 우주선 연산자
두 값의 순서를 결정하는데 사용된다.
주어진 표현식의 평가 결과가 비교 대상이 되는 값과 같은지, 그보다 크거나 작은지 알려준다.
세가지 결과 중 하나를 알려주기 때문에 부울타입을 리턴하지 않고 <compare>에 정의되고 std 네임스페이스에 속한
열거 타입으로 리턴한다.
//피연산자가 정수타입. 강한순서
strong_ordering::less // 첫 번째 피연산자가 두 번째 피연산자보다 작다.
string_ordering::greater // 첫 번째 피연산자가 두 번째 피연산자보자 크다.
string_ordering::equal // 두 피연산자가 같다.
//피연산자가 부동소수점 타입. 부분 순서
partial_ordering::less // 첫 번째 피연산자가 두 번째 피연산자보다 작다.
partial_ordering::greater // 첫 번째 피연산자가 두 번째 피연산자보다 크다.
partial_ordering::equivalent // 두 피연산자가 같다.
partial_ordering::unordered // 두 피연산자 중 하나는 숫자가 아니다.
//자신이 직접 정의한 타입에 대해 3방향 비교연산을 구현. 약한 순서
weak_ordering::less // 첫 번째 피연산자가 두 번째 피연산자보다 작다.
weak_ordering::greater // 첫 번째 피연산자가 두 번째 피연산자보다 크다.
weak_ordering::equivalent // 두 피연산자가 같다.
<compare>에는 순서의 결과를 해석해주는 이름 있는 비교 함수가 있다.
a <=> b
std::is_eq() //a와 b가 같은가?
std::is_neq() //a와 b가 다른가?
std::is_lt() //a가 b보다 작은가?
std::is_lteq() //a가 b보다 작거나 같은가?
std::is_gt() //a가 b보다 큰가?
std::is_gteq() //a가 b보다 크거나 같은가?
함수
모든 코드를 main()에 담으면 관리하기 힘들기 때문에 함수 단위로 나눔
특정 파일 안에서만 사용할 함수는 선언과 구현을 모두 소스 파일 안에 작성.
함수를 다른 모듈이나 파일에서도 사용한다면 선언은 모듈 인터페이스 파일로부터 익스포트, 정의는 모듈 인터페이스 파일이나 모듈 구현 파일에 작성한다.
함수 리턴 타입 자리에 auto를 넣으면 컴파일러가 알아서 타입을 추론한다.
한 함수에 return이 여러개 있으면 모두 같은 타입을 사용해야 하고
재귀호출의 경우 반드시 재귀호출이 아닌 return 문도 함께 있어야 한다.
int addNumbers(int number1, int number2)
{
cout << "Entering function" << __func__ << endl;
return number1 + number2;
}
//Entering functionaddNumbers 라는 문장이 출력됨
모든 함수는 내부적으로 __func__ 라는 로컬 변수가 정의되어 있다.
현재 함수의 이름을 저장함.
함수 오버로딩
이름은 같지만 매개변수 구성은 다른 함수를 여러개 제공한다는 뜻.
매개변수 타입, 개수 달라야 함
리턴타입으로는 구분되지 않고 반드시 매개변수가 달라야 함
int getRandomValue();
double getRandomValue();
//# 에러!
어트리뷰트
소스 코드에 벤더에서 제공하는 정보나 옵션을 추가하는 메커니즘
c++ 표준에 어트리뷰트가 추가되기 이전에는 벤더마다 이런 정보를 지정하는 방법이 달랐음
ex) __attribute__
c++11부터 [[attribute]] 와 같이 대괄호를 이용한 형식을 사용하도록 표준화되기 시작함
[[nodiscard]] // 어떤 값을 리턴하는 함수에 대해 지정할 수 있음.
[[nodiscard("Some explanation")]] //c++20부터 nodiscard 어트리뷰트에 이유를 설명하는 스트링을 추가할 수 있게됨
함수가 어떤 값을 반환하는데 그 값이 변수에 들어가지 않거나 해서 무시되면 에러를 띄워분다
[[maybe_unused]] // 뭔가 사용하지 않았을 때 컴파일러가 경고 메시지를 출력하지 않도록 설정
변수등을 선언해놓고 한번도 사용하지 않으면 컴파일러는 경고를 띄운다
이를 없애준다
[[noreturn]] // 호출 지점으로 다시 돌아가지 않는다.
프로세스나 스레드를 끝내버리는 함수를 만들고 사용할때 오류 메시지를 없애줌
[[deprecated]] // 지원 중단된 대상임
[[deprecated("Unsafe method, please use xyz")]] // 지원 중단되는 이유를 표현하는 인수를 옵션으로 지정 가능
현재 사용할 수는 있지만 권장하지 않는 대상임을 표시
[[likely]]
[[unlikely]]
if나 switch문에서 수행될 가능성이 높은 브랜치를 명시해 줄 수 있다.
(현대 컴파일러의 예측수준은 상당하기 때문에 굳이 사용자가 이것을 명시할 일은 잘 없다)
C 스타일 배열
배열은 같은 타입의 값을 연달아 저장. 배열에서 해당 위치를 이용해 값에 접근
배열을 선언할 때는 반드시 크기를 지정해야 함. 변수x 상수or 상수 표현식으로 지정해야 함.
// 0 초기화
int myArray[3] = {0};
int myArray[3] = {};
int myArray[3] {};
셋 다 가능
int myArray[] = {1,2,3};
이렇게 하면 자동으로 크기를 지정해줌
std::array
c++에서의 배열. 고정크기 컨테이너. c 스타일 배열 위에 한꺼풀 덮어쓴 것 뿐이지만, 항상 크기를 정확히 알 수 있고,
자동으로 포인터를 캐스트 하지 않아 버그를 방지할 수 있고 이터레이터로 반복문을 쉽게 작성할 수 있다.
<타입, 크기>
C++는 CTAD(클래스 템플릿 인수 추론)을 지원하므로 auto 타입을 쓰는것 처럼
초기자를 사용할 때 <> 사이에 타입을 지정하지 않아도 알아서 해준다
array arr {9,8,7};
이런경우 초기자 {9,8,7}을 통해 자동으로 array<int> 라고 추론해 준다.
std::vector
크기가 고정되지 않은 컨테이너
C 스타일의 배열보다 훨씬 안전하고 유연
메모리 관리를 신경 쓸 필요가 없음. 동적임
std::pair
두 값을 하나로 묶는다. first, second로 접근
std::optional
특정한 타입의 값을 가질 수도 있고, 아무 값도 가지지 않을 수도 있다.
data1,2에 값이 들어있는지 확인하려면 has_value() 매서드를 사용
실제 값을 가져오려면 value() 매서드 혹은 역참조 연산자( * )를 사용한다.
#값이 없는 optional에 대해 값을 호출하면 std::bad_optional_access 예외가 발생한다.
value_or()을 사용하면 값이 있으면 값을 리턴, 값이 없으면 다른 값을 리턴한다
구조적 바인딩
여러 변수를 선언할 때 array, struct, pair 등에 담긴 원소들을 이용하여 변숫값을 한꺼번에 초기화 할 수 있다.
array values { 11, 22, 33}; // 배열 선언
auto [x, y, z] { values }; // 구조적 바인딩으로 x y z를 array 값으로 초기화
~102p
'전문가를 위한 C++' 카테고리의 다른 글
2025/02/24 ch02. 스트링과 스트링 뷰 다루기 (0) | 2025.02.24 |
---|---|
2025/02/22 ch02. 스트링과 스트링 뷰 다루기 (1) | 2025.02.22 |
2025/02/21 (0) | 2025.02.21 |
2025/02/20 (0) | 2025.02.20 |
2025/02/18 (0) | 2025.02.18 |