포스트

C++

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. 일반 템플릿
    1
    2
    
     template <typename T>
     class MyClass { //int, char 등
    
  2. 템플릿 특수화
    1
    2
    
    template <> 
    class MyClass<double> {  //doule형만
    
  3. 템플릿 부분 특수화
    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)

  1. 컨테이너 (Containers)
  2. 알고리즘 (Algorithms)
  3. 반복자 (Iterators)
  4. 함수자 (Functors)


컨테이너


std::vector vs std::array

 std::vectorstd::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)

: 객체의 생명주기를 통해 자원의 관리(획득과 해제)를 자동화


핵심 개념 :

  • 객체 생성시 자원 획득, 소멸시 자원 해제
  • 명시적 해제 코드 없어도 메모리, 자원 누수 방지


스마트 포인터 관련

  1. 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;
}


  1. 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;
}


형변환

  1. static_cast
  2. dynamic_cast

이동의미론