Effective C++에서 사용되는 용어들에 대해 알아보자 (2) 💨
Effective C++ 책을 읽고 정리하고자 합니다.
궁금한 점, 보안점 남겨주시면 감사하겠습니다 🙇
해당 포스팅에서는 아래와 같은 내용을 다룹니다.
- 용어 사용
- 이름짓기 규약
- 스레드
용어 사용
이전 포스팅에 이어 용어정리를 마저 해보자.
복사생성자 (copy constructor)란 어떤 객체의 초기화를 위해 그와 같은 타입의 객체로부터 초기화 할 때 호출 되는 함수(생성자) 이다.
복사대입연산자 (copy assignment operator)란 같은 타입의 다른 객체에 어떤 객체의 값을 복사하는 용도로 쓰이는 함수이다.
생성자와 연산자는 초기화 유무에 따라 불리는 시점이 다르다.
class Widget {
public:
Widget (); // 기본생성자
Widget (const Widget& rhs); // 복사생성자
Widget& operator= (const Widget& rhs); // 복사대입연산자
};
Widget w1; // 기본 생성자 호출
Widget w2(w1); // 복사 생성자 호출
w1 = w2; // 복사 대입 연산자 호출
위의 예시를 통해 복사생성자와 복사대입연산자 가 불리는 시점을 확인해보자. w2
같은 경우 객체를 생성과 동시에 같은 타입의 객체를 인자로 전달해 초기화함으로 복사생성자가 호출이 된다. w1
같은 경우 기본 생성자로 우선 객체를 초기화 한 후 이후에 w2
의 객체의 값을 복사하기 때문에 해당 시점에는 복사대입연산자가 호출된다.
언뜻 보기에 대입문 처럼 보이는 것도 눈을 씻고 찾아봐야하는 경우가 있다. “=” 문법은 복사생성자를 호출하는데도 사용할 수 있기 때문이다. 아래의 예시를 보자.
Widget w3 = w2; // 생성과 동시에 초기화 되기 때문에 복사생성자가 호출 됨
복사생성자 와 복사대입연산자의 구분은 “객체가 새로정의 되냐” 즉, 초기화가 필요하냐에 따라 불리는 함수가 구별된다. “초기화” 부분을 잘 기억하자.
복사생성자는 중요도에 있어 꽤 각별한 함수이다. (나중에 복사 관련된 부분만 따로 정리할 것이다..) 값에 의한 전달 즉, Call by value라는 것을 우리는 많이 들어봤을 것이다. (C++ 책을 좀 읽었다는 가정하에..🙄) 값에 의한 객체 전달을 정의해주는 함수가 바로 복사생성자이다.
bool hasAcceptableQuality(Widget w);
Widget aWidget;
if (hasAcceptableQuality(aWidget)) ...
위의 코드와 같이, 매개변수 w
는 hasAcceptableQuality 함수에 넘어오는 값으로 정의 된다. 실제 호출에서 aWidget
이 hasAcceptableQuality 함수에 값으로 전달 될 경우 Widget의 복사생성자가 호출된다. “값에 의한 전달은 복사생성자 호출”이라는 점을 잘 기억해야한다.
미정의 동작 (undefined behavior)란 정의되어있지 않음을 의미한다. Java 혹은 C#을 하다가 C++를 배우려고 할때, 프로그래머들이 가장 당황하는 개념이다. C++에서 사용되는 구문요소들 중 몇 개는 이런저런 이유로 정의되어있지 않은 것들이 존재한다. 쉽게 말해, 실행 시간에 어떤 현상이 터질지 정확하게 예측을 할 수 없다는 의미이다.
int *p = 0; // p는 null pointer이다.
std::cout << *p; // 만약 p를 역참조한다면 어떻게 될까? 에러가 날까?
// 아니다. 해당 동작은 미정이 동작으로, 에러가 날수도 아닐 수도 있다.
char name[] = "Darla"; // 문자열 배열을 하나 할당받는고 가정하자.
// 크기가 6인 배열 (마지막에 null을 안붙이는 건 끔찍하다.)
char c = name[10]; // 유효하지 않은 index (원소지정번호)에 접근할 경우..
// 이거 또한 날수도 있고 아닐수도 있다.
미정의 동작 이란 잘돌아가든지 어쩔 땐 전혀 엉뚱한 결과를 낸다던지 등의 알 수 없는 동작을 한다. 고수의 프로그래머들은 미정의 동작은이 하드 드라이브까지 날려버릴 수 있다고 조큼.. 과장되게 이야기한다. C++ 프로그램을 잘 만드는 사람들은 미정의 동작과 멀리 떨어져 가는 코드를 만드는데 최선을 다하는 사람들이다.
이름짓기에 대한 규약
해당 책에서 이름을 어떻게 짓는지에 대해 알아보자.
- 책에서는 주석에서 생성자와 소멸자를 ctor, dtor 약어로 표기할 것이다.
- 책에서 잘 쓰는 이름은 lhs “좌변 (left-hand side)” 와 rhs “우변 (right-hand side)” 이다.
- 이항 연산자를 사용할 때 저자가 자주 사용하는 이름이다.
// 유리수를 곱하는 연산을 한다고 가정해보자. a * b operator*(a,b) // 해당 함수위 위의 연산은 동일하다. // 위의 정리한 약어와 동일하게 lhs, rhs를 아래와 같이 사용한다. const Rational operator* (const Rational& lhs, const Rational& rhs);
- 연산자 함수가 멤버함수 로 만들어진 경우에는 좌변 인자가
this
로 사용되기 때문에 매개변수의 이름은 rhs만 있으면 된다.
- 포인터 이름을 지을 때에는 타입 T에 관한 포인터 pt 라고 부른다. 즉, T에 대한 포인터 (pointer to T) 라는 의미이다.
Widget *pw; // pointer to Widget class Airplane; Airplane *pa; // pointer to Airplane
- 위의 예시와 같이 사용하며, reference도 동일하게 사용될 것이다.
스레드
해당 책은 C++11 이전에 나온 책으로 멀티 thread에 대한 개념을 담고 있지 않다. 하지만 C++ 구문 요소 중 thread 환경에서 문제를 일으킬 만한 부분들을 지적해서 알려주는 쪽으로 정의한다.