-
용어 사용에 대하여
- 선언(declaration)은 코드에 사용되는 '어떤 대상'의 이름과 타입을 컴파일러에게 알려 주는 것이다. 하지만 구체적인 세부사항은 선언에 들어 있지 않다.
1) 객체 선언 : extern int A;
2) 함수 선언 : std::size_t A(int B);
3) 클래스 선언 : class A;
4) 템플릿 선언 : template<typename T>
Class GraphNode;- 모든 함수의 선언문에는 시그니처(signature), 다시 말해 그 함수의 매개변수 리스트와 반환타입이 나와있다. 함수의 경우엔 시그니처가 그 함수의 타입이다. 앞에서 본 A 함수의 시그니처는 std::size_t A(int)이다. 다시 말하면 'int 하나를 취하고, std::size_t를 반환하는 함수'라는 의미이다.
- 정의(definition) 는 선언에서 빠진 구체적인 세부사항을 컴파일러에게 제공하는 것이다. 객체의 경우에 있어서 정의는 컴파일러가 그 객체에 대한 메모리를 마련해 놓는 부분이 된다. 함수나 함수 템플릿에 대한 정의는 그들에 대한 코드 본문(body)을 제공하는 것이다. 클래스 혹은 클래스 템플릿의 경우에는 그 클래스 혹은 템플릿의 멤버를 넣어 준 결과가 정의이다.
- 초기화(initialization) 는 어떤 객체에 최초의 값을 부여하는 과정이다. 사용자 정의 타입으로 생성한 객체의 경우, 초기화는 생성자에 의해 이루어진다. 기본 생성자(default constructor) 는 어떤 인자도 주어지지 않은 채로 호출될 수 있는 생성자이다. 원래부터 매개변수가 없거나 모든 매개변수가 기본 값을 갖고 있으면 기본 생성자가 될 수 있다.
- explict 로 선언된 생성자는 프로그래머가 예상하지 못했던(바라지 않는) 타입 변환을 막아준다. 암시적 타입 변환에 생성자가 사용될 여지를 남겨둘 뚜렷한 이유가 없는 한, 생성자는 explicit 선언을 우선적으로 하자.
-
복사 생성자(copy constructor) 는 어떤 객체의 초기화를 위해 그와 같은 타입의 객체로부터 초기화할 때 호출되는 함수이고,복사 대입 연산자(copy assignment operator) 는 같은 타입의 다른 객체에 어떤 객체의 값을 복사하는 용도로 쓰이는 함수이다.
class A{
public:
A(); // 기본 생성자
A(const A& B); // 복사 생성자
A& operator=(const A& B); // 복사 대입 연산자
};A a1; // 기본 생성자 호출
A a2(a1); // 복사 생성자 호출
a1 = a2; // 복사 대입 연산자 호출
A a3 = a2; // 복사 생성자 호출 -
복사 생성자는 그 중요도에 있어서 꽤나 각별한 함수이다.
값에 의한 객체 전달을 정의해주는 함수가 바로 복사 생성자이기 때문이다. 다음 예를 보자bool testFunction(A rA);
. . .
A tA;
if (testFunction(tA)) ...- 해당 코드에서 매개변수 tA는 testFunction 함수에 값으로 넘겨지도록 되어 있으므로, 실제 호출에서 rA는 tA가 복사된다. 이때 수행되는 복사에 A의 복사 생성자가 쓰이는 것이다. '값에 의한 전달(pass-by-value)'은 '복사 생성자 호출'이라고 이해하면 된다.
(하지만 사용자 정의 타입을 값으로 넘기는 발상은 일반적으로 좋지 않다고 알려져 있다. 그보다는 '상수 객체에 대한 참조로 넘기기'가 더 좋다) - STL은 표준 템플릿 라이브러리(Standard Template Library)의 준말이다. 이것은 C++의 표준 라이브러리에 속해 있는 라이브러리인데 컨테이너(vector, list, set, map 등), 반복자(vector::iterator, set::iterator 등), 알고리즘(for_each, find, sort 등) 및 이들과 관련된 기능들이 집결한 결정체이다.
여기서 컨테이너/반복자/알고리즘에 관련된 기능들의 상당 부분을 이른바 함수 객체(function object), 즉 함수처럼 동작하는 C++ 객체가 차지하고 있다. 이 함수 객체는 함수 호출 연산자인 operator()를 오버로드한 클래스로 만든다. - 자바 혹은 C#을 하다가 C++을 배우려는 프로그래머들이 당황하는 부분이 미정의 동작(undefined behavior) 이다. C++에서 사용되는 구문요소들 중 몇 개는 이런저런 이유로 인해 동작 자체가 글자 그대로 '정의되어 있지 않다'. 쉽게 말해, 실행 시간에 어떤 현상이 발생할지 확실히 예측할 수 없다는 뜻이다. 다음은 해당 상황에 대한 예제 코드 2가지이다.
int *p = 0; // p는 널 포인터다
std::cout << *p; // 널포인터를 역참조하면 미정의 동작이 발생한다.char name[] = "GYUT"; // name은 크기가 4인 배열이다.
char a = name[10]; // 유효하지 않은 배열 원소지정번호(index)로 참조하면 미정의 동작이 발생한다
-
다른 언어로 생활을 하다가 C++로 온 프로그래머들이 헷갈려 하는 용어가 하나 더 있다. 바로 인터페이스(Interface) 이다. 자바 및 닷넷 계열 언어의 경우에는 Interface라는 것이 언어 차원에서 주어져 있지만, C++에는 그런게 없다. C++로도 인터페이스와 흡사하게 만들 수는 있다. 공부를 진행해가면서 앞으로 말하는 인터페이스는 지극히 평범하고 일반적인 설계 아이디어로서의 인터페이스다.
-
다음은 사용자(client) 라는 용어이다. 이 책에서 '사용자'는 직접 만든 코드(인터페이스가 되겠다)를 사용하는 아무개 혹은 아무것이다. 예를 들어 함수의 사용자는 그 함수를 사용하는 모든 대상이 해당된다. 그 함수를 호출하는(혹은 주소를 취하는) 코드, 그런 코드를 작성했거나 유지보수하는 사람 모두가 사용자이다. 클래스 혹은 템플릿의 사용자는 그 클래스/템플릿을 사용하는 소프트웨어의 코드도 되고, 그 코드를 작성하고 유지보수하는 프로그래머도 된다.
-
코드 주석문에서 생성자와 소멸자를 언급할 때는, 약자표기인 ctor 및 dtor 를 사용한다.
- 해당 코드에서 매개변수 tA는 testFunction 함수에 값으로 넘겨지도록 되어 있으므로, 실제 호출에서 rA는 tA가 복사된다. 이때 수행되는 복사에 A의 복사 생성자가 쓰이는 것이다. '값에 의한 전달(pass-by-value)'은 '복사 생성자 호출'이라고 이해하면 된다.
-
스레딩에 대한 고려사항
-
C++는 언어 차원에서 스레드에 대한 개념 자체가 없다. 어떤 종류의 병행성도 고려하고 있지 않은 언어이다. C++의 표준 라이브러리도 마찬가지이다. C++이 관여하고 있는 한, 다중스레드 프로그램이란 것은 존재하지 않는다.
-
하지만 실질적으로 그렇지 않은 게 우리의 일상이기도 하다. 하지만 이번 공부에서는 최대한 다중스레드를 생각하지 안혹, 단일 스레드 위주로 진행할 것이다.
-
-
TR1 그리고 부스트
- TR1("Technical Report 1") : C++ 표준 라이브러리에 새로 추가되는 기능들에 대한 명세이다. 새로 추가된 기능들은 클래스 및 함수 템플릿이 주류인데, 해시 테이블(hash table), 참조 카운팅 방식 스마트 포인터(reference-counting smart pointer), 정규 표현식(regular expression) 등이다. TR1의 구성요소는 tr1 네임스페이스에 들어 있고, tr1 네임스페이스는 std 네임스페이스 안에 중첩되어 있다.
- 부스트 : 비슷한 공력의 고수들 사이에서 교차 검증을 거쳤고, 플랫폼 간 이식성도 가진 오픈소스 C++ 라이브러리를 제공하는 단체이다. 이 단체의 웹사이트(http://boost.org)를 가리키기도 한다. TR1의 구성요소 중 대부분은 바로 이곳에서 만들어졌다.
'Programming > C & C++' 카테고리의 다른 글
EC++ Study_3 (0) | 2019.09.01 |
---|---|
EC++ Study_2 (0) | 2019.08.11 |
C# Windows Forms Application GUI 기본 (0) | 2018.07.27 |
Visual C++ Windows forms [1] (0) | 2018.07.06 |
Visual C++ 학습 (0) | 2018.07.05 |