C++
C++
C++
std::for_each
1
std::for_each(first, last, func);
예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <algorithm>
void print(int num) {
std::cout << num << " ";
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
// for_each를 사용하여 배열의 각 요소를 출력
std::for_each(std::begin(arr), std::end(arr), print);
return 0;
}
배열 출력
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
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <algorithm> // std::for_each, std::copy 사용을 위한 헤더
#include <iterator> // std::ostream_iterator, std::begin, std::end 사용을 위한 헤더
int main() {
int arr[] = { 1, 2, 3, 4, 5 };
int n = sizeof(arr) / sizeof(arr[0]); // 배열 크기 계산
// 1. 전통적인 for 루프 사용
std::cout << "1. 전통적인 for 루프:" << std::endl;
for (int i = 0; i < n; ++i) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
// 2. std::begin과 std::end 사용
std::cout << "2. std::begin과 std::end 사용:" << std::endl;
for (auto it = std::begin(arr); it != std::end(arr); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// 3. std::for_each와 람다 함수 사용
std::cout << "3. std::for_each와 람다 함수 사용:" << std::endl;
std::for_each(std::begin(arr), std::end(arr), [](int num) {
std::cout << num << " ";
});
std::cout << std::endl;
// 4. 범위 기반 for 루프 사용
std::cout << "4. 범위 기반 for 루프 사용:" << std::endl;
for (int num : arr) {
std::cout << num << " ";
}
std::cout << std::endl;
// 5. std::copy와 std::ostream_iterator 사용
std::cout << "5. std::copy와 std::ostream_iterator 사용:" << std::endl;
std::copy(std::begin(arr), std::end(arr), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
return 0;
}
레퍼런스 Reference
변수나 객체를 직접 다루지 않고, 해당 변수나 개체에 대한 별칭(alias)
을 만들어주는 기능
1
2
3
4
타입& 변수명;
int a = 10;
int& ref = a;
값 복사가 아닌 원본을 다룸 -> 동일 메모리 주소 참조 (&a == &ref
)
1
2
&a = 000000000014FCD4
&ref = 000000000014FCD4
간접 참조 & 직접 참조
간접 참조 Pointer | 변수 자체와 동일 |
직접 참조 Reference | 주소를 통한 간접적 접근 |
레퍼런스 vs 포인터
reference는 NULL이 없어서 안정 pointer는 NULL이 가능해서 불안정 가능성이 있음
정보은닉
const
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <string>
class Person {
public:
std::string name = "";
private:
int age = 0;
public:
void setAge(int a) { age = a; }
int getAge() const { return age; } //const : 멤버 변수 수정 불가
};
int main() {
Person p;
p.name = "정예림";
p.setAge(19);
std::cout << p.name << "의 나이는 " << p.getAge() << "입니다." << std::endl;
return 0;
}
멤버랑 함수 같이 -> Encapsulation private 멤버는 함수로만 접근 가능 : data hiding
constexpr
컴파일 시간에 값이 결정
컴파일 시간에 모든 연산하여 프로그램 실행 중 연산 x
일반 함수 :
일반 함수는 런타임에 호출되고 실행
실행시 스택에 호출 스택 프레임을 생성하고 계산
특징 | 일반 함수 | constexpr 함수 |
---|---|---|
계산 시점 | 런타임 | 컴파일 시간 또는 런타임 |
컴파일 시간 호출 | 불가능 | 가능 |
제약 조건 | 없음 | 컴파일 시간에 평가 가능한 연산만 허용 |
성능 최적화 | 없음 | 컴파일 시 계산으로 성능 향상 가능 |
동적 메모리, I/O 사용 | 가능 | 불가능 |
람다 표현식
일반 함수와 동일하게 함수 스택 프레임 생성 후 해제
기본 문법
1
2
3
[capture](parameter_list) -> return_type {
// function body
};
장점 | 단점 |
---|---|
1. 간결성 | 1. 디버깅 어려움 |
2. 임시 함수 사용 | 2. 확장성 제한 |
3. 익명 객체 전달 | 3. 코드 유지 보수 어려움 |
4. 가독성 | 4. 제한된 범위의 데이터 접근 |
5. 동적 코드 작성 |
예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;
int main() {
auto add = [](int a, int b) {
return a + b;
};
/* 둘다 가능
auto add = [](int a, int b) -> int {
return a + b;
};
*/
cout << "3 + 4 = " << add(3, 4) << endl;
return 0;
}
캡처 사용
[]
: 람다의 캡처 리스트
[=]
: 모든 외부 변수 캡처(복사)
[x, y]
: 람다 내부에서 외부 변수 x
, y
를 사용할 수 있게 캡처
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;
int main() {
int x = 10;
int y = 20;
auto sum = [x, y]() {
return x + y;
};
/* 둘다 가능,
auto sum = [=]() {
return x + y;
};
*/
cout << "Sum of x and y: " << sum() << endl;
return 0;
}
바로 호출
()
함수 호출 연산자로 바로 호출
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
using namespace std;
int main() {
auto add = [](int a, int b) {
return a + b;
};
cout << "3 + 4 = " << add(3, 4) << endl;
[]() {
cout << "hi" << endl;
}();
}
템플릿 Template
함수 템플릿
다양한 자료형에 대한 동일 기능 확장성 제공
컴파일 시점에 생성 : 런타임 오버헤드 x
기본 문법
1
2
3
4
5
template <typename T>
T template_name(T arg) {
//내용
return;
}
예제 코드 보기
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
#include <iostream>
using namespace std;
template <typename T>
T add(T x, T y) {
return (x + y);
}
int main(void) {
int a1 = 1, b1 = 2;
int res1 = add<int>(a1, b1); //<int> 빼도 됨
cout << res1 << endl;
double a2 = 1.1, b2 = 2.2;
double res2 = add<double>(a2, b2); //<double> 빼도 됨
cout << res2 << endl;
std::string s1 = "abc";
std::string s2 = "def";
std::string res3 = add<std::string>(s1, s2); //<std::string> 빼도 됨
cout << res3 << endl;
return 0;
}
클래스 템플릿
- 일반 템플릿
1 2
template <typename T> class MyClass { //int, char 등
- 템플릿 특수화
1 2
template <> class MyClass<double> { //doule형만
- 템플릿 부분 특수화
1 2
template <typename T> class MyClass<T*> { //int*, char* 등
매칭 우선순위
특수화 > 부분 특수화 > 일반 템플릿
코드 보기
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
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
using namespace std;
//일반 템플릿 : int, char 등
template <typename T>
class MyClass {
public:
void display() {
cout << "General" << endl;
}
};
//템플릿 특수화 : double만
template <>
class MyClass<double> {
public:
void display() {
cout << "double" << endl;
}
};
//템플릿 부분 특수화 : int*, double* 등
template <typename T>
class MyClass<T*> {
public:
void display() {
cout << "pointer" << endl;
}
};
int main() {
MyClass<int> obj1;
obj1.display();
MyClass<double> obj2;
obj2.display();
MyClass<int*> obj3;
obj3.display();
return 0;
}
STL (Standard Template Library)
- 컨테이너 (Containers)
- 알고리즘 (Algorithms)
- 반복자 (Iterators)
- 함수자 (Functors)
컨테이너
std::vector
vs std::array
std::vector | std::array | |
---|---|---|
동적 크기 | 가능 (자동으로 확장/축소) | 불가능 (고정 크기) |
메모리 관리 | 동적 관리(힙) | 스택/정적 메모리 관리 |
추가/삭제 | 가능 | 불가능 |
성능 | 약간의 오버헤드 (메모리 관리 필요) | 빠름 (고정 크기) |
유연성 | 높음 | 낮음 |
std::vector
1
2
std::vector<int> v1 = { 1,2,3,4,5 }; //1,2,3,4,5를 가지는 벡터 v1 생성
std::vector<int> v2(10, 3); //3의 값 10개를 가지는 벡터 v2 생성
1
2
3
4
5
6
7
8
9
10
11
12
auto it1 = v1.begin(); //v1.begin() : 벡터의 첫번째 요소 위치 가리킴
std::cout << *it1 << endl;
auto it2 = v1.end(); //v1.end() : 벡터 크기보다 1 커진 위치 가리킴
std::cout << *(it2 - 1) << endl; // 마지막 원소 출력
v1.resize(2); //v1.resize() : v1 벡터 크기를 2로 줄임
std::cout << v1.size() << endl; //v1.size() : v1 벡터의 사이즈 출력
v1.shrink_to_fit(); //원래 크기로 다시 돌려놓음
std::cout << vec.size() << endl;
vector size
vector capacity
예외 try-catch
try-catch
:
프로그램 내에서 예상치 못한 문제, 예외 상황이 발생했을 때, 이를 감지하고 적절히 처리하는 방법, 프로그램이 종료되는 것을 방지
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <stdexcept> // std::invalid_argument
int divide(int a, int b) {
if (b == 0) { //잘못된 인수 -> thorw : std::invalid_argument의 예외를 발생(던짐)
throw std::invalid_argument("Division by zero is not allowed.");
}
return a / b;
}
int main() {
try { //예외를 발생시킬 수 있는 코드 실행(시도)
int result = divide(10, 0); //0으로 나눴음 //<-----예외 발생 지점
std::cout << "Result: " << result << std::endl;
}
catch (const std::invalid_argument& e) { //<-----여기서 예외를 잡을 것임
std::cerr << "Error: " << e.what() << std::endl;
}
catch (...) {
std::cerr << "An unknown error occurred." << std::endl;
}
return 0;
}
스마트 포인터 Smart Pointer
C : 원시 포인터
C++ : 스마트 포인터
개발자가 delete를 깜빡해도 자동으로 메모리 해제
1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <memory>
int main() {
//unique_ptr : 특정 메모리 블록을 소유 (단독 소유권)
std::unique_ptr<int> smart_ptr(new int(11)); //int형 값 11 동적으로 생성
std::cout << *smart_ptr << std::endl;
return 0;
//명시적 해제 없어도 스코프를 벗어나면(smart_ptr 소멸시) 자동으로 메모리 해제
}
스마트 포인터 종류
스마트 포인터 | 특징 | 소유권 | 참조 카운트 | 주요 사용 사례 |
---|---|---|---|---|
std::unique_ptr | 단독 소유 | 단독 소유 | 없음 | 특정 메모리 리소스를 단독으로 소유해야 할 때 |
std::shared_ptr | 공유 소유 | 공유 소유 | 있음 | 여러 객체가 리소스를 공유해야 할 때 |
std::weak_ptr | 공유 소유를 참조하지만 소유권 없음 (순환 참조 방지) | 없음 | 없음 | shared_ptr 와 함께 사용 |
스마트 포인터 선택 기준
- 단독 소유:
std::unique_ptr
. - 공유 소유:
std::shared_ptr
. - 순환 참조 방지:
std::weak_ptr
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <memory>
int main() {
int val = 11;
std::unique_ptr<int> a = std::make_unique<int>(val);
std::unique_ptr<int> b = std::make_unique<int>(val);
std::cout << &val << std::endl; //000000000014FBE4
std::cout << a << std::endl; //0000000000435E40
std::cout << b << std::endl; //0000000000436280
std::shared_ptr<int> c = std::make_unique<int>(val);
std::shared_ptr<int> d = c;
std::cout << c << std::endl; //0000000000435C80
std::cout << d << std::endl; //0000000000435C80
return 0;
}
RAII (Resource Acuisition Is Initialization)
: 객체의 생명주기를 통해 자원의 관리(획득과 해제)를 자동화
핵심 개념 :
- 객체 생성시 자원 획득, 소멸시 자원 해제
- 명시적 해제 코드 없어도 메모리, 자원 누수 방지
스마트 포인터 관련
std::make_unique
스마트 포인터 쉽게 초기화
1
2
3
4
5
6
7
8
#include <iostream>
#include <memory>
int main() {
auto ptr = std::make_unique<int>(42); // int 객체 생성 및 unique_ptr로 감싸기
std::cout << *ptr << std::endl; // 출력: 42
return 0;
}
std::move
소유권 이전, 원본 객체 유효x
int형 값 10의 소유권 : ptr1 -> ptr2, ptr1 == nullptr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int> ptr1 = std::make_unique<int>(10); // 스마트 포인터 소유
std::unique_ptr<int> ptr2 = std::move(ptr1); // 소유권 이전
if (ptr1 == nullptr) {
std::cout << "ptr1 is null." << std::endl;
}
std::cout << *ptr2 << std::endl; // 10
return 0;
}
형변환
- static_cast
- dynamic_cast