[C++] 객체지향프로그래밍1 : Lambda Expression / Namespace / ADL / Nested Class (chapter 17 - 18)
이 게시글은 대학교 객체지향프로그래밍1 (C++) 강의를 수강하며 교수님의 강의를 정리한 것입니다. 오직 개인적인 학습 기록으로 활용하기 위한 용도로 작성되었으며, 가독성이나 체계적인 정리가 부족할 수 있습니다.
Lambda Expression
지금까지는 코드 안에 함수 호출을 기재했다.
다시 말해, 어떤 함수가 호출될지를 컴파일 시간에 안다고 가정한 것.
버튼을 구현하는데,
” 로딩 중이 아니라면 - 터치가 되지만
로딩 중이라면 - 기다리라는 메시지 “
를 노출하는 것과 같이 어떤 함수가 호출될지를 실행 시간에 결정하려면 어떻게 해야할까?
즉 호출할 함수를 동적으로 결정해야 할 때 ~
Lambda
(함수 객체 중) 이름이 없는 함수 객체
🚨 함수가 아니라 함수 객체
** [캡쳐리스트] (매개변수) ->반환타입 { 함수 몸통 } ** 와 같은 형식.
매개변수가 없다면 생략 가능
1
2
3
auto lambda = []() {
cout << "hello lambda" << endl;
|;
1
lambda();
lambda 라는 변수에 대입만 한 것이므로 호출은 다음처럼 한다.
[ ] : lambda 캡쳐리스트
람다 표현식의 몸통에는
- 전역 변수
- 매개변수
- 람다 안에서 정의 한 지역변수
만 쓸 수 있다.
lambda 가 정의된 scope 에서 쓸 수 있던 지역 변수 도 계속 쓸 수 있으면 편리할 것이다.
이를 캡쳐리스트 라고 대괄호 안에 넘길 변수 목록을 콤마로 구분해서 기재한다.
값으로 복사할 것 : 그냥 변수 이름을 기재
참조를 넘길 것 : 변수 이름 앞에 & 을 붙인다.
( 출처 : 객체지향프로그래밍1 문대경 교수님 강의자료 중 일부 )
lambda 자체의 타입
Lambda 를 정의할 때는 auto 를 사용하면 편리
그러나, lambda 를 다른 함수에 인자로 넘길 때는, 명시적으로 lambda 타입을 기재해야 한다.
- #include <functional> 을 한다.
- lambda 타입을 std::function<반환타입(매개변수 목록)> 로 쓴다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <functional>
#include <iostream>
using namespace std;
void wrapper (const functional<void(int)> &filter, int v) {
filter(v);
}
int main(void) {
어쩌고 저쩌고
auto filter [](int v) {
cout << v << endl;
};
어쩌고 저쩌고
}
lambda 가 참조를 캡쳐했는데 lambda 자체를 복사해서 호출할 때는 scope 이 바뀌어 참조 대상이 사라질 수 있다.
call by value 로 캡쳐했는데 변경하고 싶을 때
temp temp temp temp !!!
함수의 기본 인자 (Default Argument)
아무 인자나 기본 값을 정할 수 있는 것이 아니다.
맨 마지막 것부터 기본 인자를 줄 수 있다.
1
void f(int a, int b = 10(;
이름 공간 (Namespace)
symbol(=이름)
compiler 가 기계어 파일인 오브젝트 파일 생성
linker 는 오브젝트 파일들을 모아서 심볼
심볼, 즉 이름은 오직 한번만 사용할 수 있다.
중복되는 symbol 을 피하는데 한계
특히 외부에서 가져온 라이브러리를 쓸 때, 라이브러리 개발자가 사용한 이름은 우리가 어쩔 수 없다.
의도적으로 cpp 구현을 여러 개의 버전으로 관리하고 싶을 수 있다.
심볼들을 논리적인 단위로 묶어 구분, 이를 “이름 공간” 이라고 함.
namespace 의 특징
- 같은 namespace 안에 정의된 심볼은 같은 scope 안에 있음. 별다른 표기 없이 사용 가능.
- namespace 밖에서는 그 안의 심볼들이 보이지 않는다. (접근 불가, 범위지정 연산자 :: 를 사용해야 한다)
- namespace 안에 중첩되는 또 다른 namespace 정의 가능
namespace 정의
class 와 달리 뒤에 ; 가 안붙음
1
2
3
4
5
6
7
8
9
namespace MyNamespace {
int var;
void f();
class MyClass {
public:
int a;
};
}
namespace 는 한번 정의하면 거기서 끝난게 아니라
열려 있는 상태로 계속해서 심볼을 추가할 수 있음.
namespace 안에 변수/함수/멤버 함수 정의하기
- header 파일에는 변수/함수의 선언과 클래스 정의
- cpp 파일에는 변수/함수/멤버함수의 정의를 둔다.
cf) myfile.h
1
2
3
4
5
6
7
8
9
namespace N {
extern int var;
void f();
class A {
public:
void g();
};
}
namespace N {
int var;
void f() { cout « “f\n”; }
void A::g() { cout « “A::g\n”; }
1
2
3
4
- ### 방법\#2 - 범위 지정자를 써서 명시적으로 한정하기
```cpp
int N::var;
범위지정자를 이용해 네임스페이스 내부 심볼 이용하기
1
2
3
4
5
6
#include "myfile.h"
int main (void) {
N::A obj;
obj.g();
}
범위 지정 연산자 :: 를 namespace 없이 쓰면 전역 영역을 가르킴.
using 선언
매번 범위 지정 연산자로 한정하는 것은 귀찮다.
앞으로 언급되는 이 특정 심볼은 namespace 안의 특정 심볼임을 명시.
1
2
3
4
5
using std::cout;
int main(void) {
cout << "hello" << endl;
}
using 은 scope 의 적용을 받는다.
using 지시자 (Using - directive)
전체 namespace 를 using 으로 지정할 수도 있음. “ using 지시자 “ 라고 한다.
1
using namespace std;
**🚨 using 을 남발하는 것은 좋은 습관이 아님.
전역 영역에 넣는 것은 권장하지 않음. **
header file 에도 마찬가지. ( 해당 파일을 include 하는 모든 파일에 영향을 줌 )
using 의 활용 예시 ( 구현의 버전 스위칭 )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
namespace version1 {
void f();
}
namespace version2 {
void f();
}
int main (void) {
구현의 버전을 아래 using 으로 통제
using namespace version1;
위의 using 에 따라 선택된다.
f();
}
1
namespace 별명(Alias)을 이용한 축약
namespace 가 길 때 범위 지정 연산자를 매번 쓰는 것은 귀찮을 수 있다.
namespace 에 다른 이름을 붙여서 사용 가능.
1
2
3
4
5
namespace S = std;
int main (void) {
S::cout << "hello";
}
구현의 버전을 스위칭 하는 용도로 활용가능.
1
2
3
4
5
6
7
8
9
10
11
namespace version1 {
void f();
}
namespace version2 {
void f();
}
int main (void) {
namespace current = version1;
currrent::f();
}
Unnamed Namespace (익명 namespace)
namespace 에 이름을 주지 않으면 익명 namespace
해당 namespace 는 한 파일 안에서만 동작한다.
Nested Namespace (중첩 namespace)
namespace 안에서 새로운 namespace 를 선언할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
namespace N {
void f();
namespace internal {
int var;
}
}
int main (void) {
N::f();
N::internal::var = 10;
}
Namespace 조합 (Namespace Composition)
서로 다른 namespace 에 흩어져 있는 심볼을 뽑아서 새 namespace 를 만들 수 있다.
양쪽 모두 Student 라는 클래스가 있어서 이름 충돌이 발생하는 경우, using computer::Student; 로 특정 namespace 의 클래스를 사용가능.
1
2
3
4
5
6
namespace engineering {
using namespace computer;
using namespace electrical;
using computer::Student;
}
class = 일종의 namespace
Argument Dependent Lookup (ADL)
함수 이름을 찾을 때 실행 인자의 타입이 사용자 정의 타입 (클래스) 이면 이 타입이 정의된 이름공간”도” 같이 찾는 것.
ADL 보다는 멤버 함수가 우선 됨
C++ 중첩 클래스 (Nested Class)
class 정의 안에 다른 class 정의
안쪽에 정의된 클래스를 nested class 라고 한다.
Nested Class 에 대한 접근은 접근 지정자를 따른다.
해당 클래스에 한정되는 클래스를 목적에 따라 추가가능
- internal 목적이면 protected 나 private
- 외부에서 사용하게 하는 목적이라면 public
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
class A {
public:
class Nested {
public:
void f();
private:
int a;
};
void f();
private:
int a;
};
void A::f() {}
void A::Neested::f() {}
int main (void) {
A a;
a.f();
A::Nested nested;
nested.f();
}
C++ 클래스 안에 enum 정의하기
enum 은 종류를 열거할 때 이용된다. (enumerate)
1
2
3
4
5
6
7
8
9
10
11
12
class A {
public:
enum Color {
Red,
Green,
Blue,
};
};
int main(void) {
A::Color c1 = A::Blue; // ok
A::Color c2 = A::Color::Red; // ok either.



