※ C++의 버전변화

C++98, C++03  ==>  C++11  ==>  C++14 ...

 

C++11: 기존 C++98에서 람다 표현식을 지원한 것

C++14: 일반화된 함수반환 형식 유추를 지원한 것

 

 

※ C++11/14 문법적 변경 사항

1. 초기화 리스트 및 초기화 방법의 통합

2. 새로운 타입의 추가 : long long형 정수

3. 새로운 스마트 포인터 추가 

4. 널 포인터 상수 추가 : nullptr

5. 열거체의 범위 지정

6. 자동 타입 변환 : auto

7. 타입 변환 연산자 추가 : explicit

8. 범위 기반 for 문 추가

9. 람다 함수와 람다 표현식 추가

 

 

※ C++11/14  표준 라이브러리 변경 사항

1. 확장할 수 있는 난수 생성기의 추가 : random 헤더 파일

2. 튜플 템플릿의 추가: tuple 헤더 파일

3. 정규 표현식의 추가 : regex 헤더 파일

4. 다중 프로그래밍을 위한 스레드의 지원 : thread_local 키워드, automic 헤더 파일

5. 임베디드 프로그래밍을 위한 저수준 프로그래밍 지원

 

 

※ C++11의 move semantics

C++11에서 가장 널리 쓰이는 기능은 바로 이동 의미론이다. (move semantics)

이동의미론l-value와 r-value가 구분된다는 점에서 유용하다.

l-value: 이름, 포인터, 왼값 참조를 통해 지칭할 수 있는 객체 (주소를 취할 수 있음)

r-value:이동연산이 가능한 객체를 가리킴 (함수가 돌려준 임시객체)

class Widget {
public:
    Widget (Widget&& rhs);  // rhs의 형식은 r-value참조, rhs 자체는 l-value
};

여기서 유의할 점은 rvalue 참조 형식의 매개변수지만 매개변수자체는 lvalue이다.

Widget의 이동생성자 안에서 rhs의 주소를 취하고 있기 때문이다.

 

 

※ 복사생성자를 통해 생성된 복사본 vs 이동생성자를 통해 생성된 복사본

이 둘을 구분하는 용어는 없다. 아래 예시를 보면

대체로 rvalue 복사는 이동생성자로 생성되고 lvalue 복사는 복사생성자로 생성된다.

호출지점에서 함수에 전달하는 표현식을 argument, 인자라 하고 

인수는 함수의 매개변수, parameter를 초기화하는데 사용된다.

매개변수는 l-value이지만 매개변수의 초기화에 쓰이는 인수는 l,rvalue 둘 다 가능하다.

 

 

 

 

 

 

 

 

※  C++과 람다 표현식

람다표현식으로 만들어진 함수객체를 closure라 부른다.

람다표현식과 그로부터 생성된 클로저를 구분해야하는 경우는 드물다.

또한 함수템플릿과 템플릿함수를 구분하는 경우도 드물다. (클래스 템플릿과 템플릿 클래스도 마찬가지)

 

C++에서 함수는 크게 2가지로 나뉜다.

- 일반함수(General Function): 일반적인 반환타입을 갖는 함수

- 무명함수(Anonymous Function): 람다(Lambda)이지만 함수객체처럼 함수의 이름없이 사용가능한 함수

int main() {
    // 람다식 선언
    auto Func1 = [] {
        cout << "Hello, Lambda" << endl;
    };
    
    // 람다식 인수로 int n 선언
    auto Func2 = [](int n){
        cout << "val = " << n << endl;   
    };
    
    Func2(2);
}

람다는 일반 함수처럼 사전에 선언문과 정의문 없이 하나의 문장 || 다수의 문장으로 직접 정의하고 실행시킬 수 있다.

이런 람다는 1~2번 작업을 위한 임시적인 함수를 만드는데 적합하다.

 

 

※ 람다 만드는 법

[캡쳐절] { 본문 }
[캡쳐절] (인수) { 본문 }
[캡쳐절] (인수)->반환타입{ 본문 }
[캡쳐절] (인수) mutable(옵션) constexpr(옵션) 예외(옵션) 속성(옵션) -> 반환타입{ 본문 }

마지막은 C++17이후 추가된 람다형식이다. 이를 아래와 같이 표현할 수 있다.

[캡쳐 리스트](인수) mutable(optional) constexpr(optional) 예외속성 -> 반환데이터타입{ 본문 }

 

 

 

1. 캡쳐절 (Capture Clause)

람다는 일반함수에 없는 캡쳐절이 존재한다.

 

2. 인수 리스트 (Parameter List)

람다는 일반함수에 없는 캡쳐절이 존재한다.

 

3. mutable (옵션)

람다는 일반함수에 없는 캡쳐절이 존재한다.

 

4. constexpr (옵션)

람다는 일반함수에 없는 캡쳐절이 존재한다.

 

5. throw (옵션)

람다는 일반함수에 없는 캡쳐절이 존재한다.

 

6. Attribute(옵션)

람다는 일반함수에 없는 캡쳐절이 존재한다.

 

7. 반환 데이터 타입 (옵션)

람다는 일반함수에 없는 캡쳐절이 존재한다.

 

8. 본문

람다는 일반함수에 없는 캡쳐절이 존재한다.

 

 

※ C++의 입출력 연산자, [>>, <<]

C++의 입출력은 C언어처럼 printf();를 사용해도 무방하다. 다만 입출력에 대해 istream& operator >>(istream&, Coord&);와 ostream& operator<<(ostream&, const Coord&); 처럼 이미 C++의 #include<iostream>에 연산자 오버로딩이 되어 있기 때문에 std::cout << 출력하고 싶은 내용or변수 << std::endl;으로 printf(%d)와 같이 변수의 data성격에 따른 출력 타입을 고려해줄 필요가 없어졌다.

 

C와 C++의 달라진 점은 여러가지가 있는데 bool이라는 자료형 추가, 함수 및 연산자 오버로딩, 클래스, inline, Call-by-Reference 등이 있다.

 

※ C가 아닌 C++ 사용시의 장점

- classes

- inheritance (derived classes)

- strong typing

- inlining

- default arguments

 

※ C++은 언어들의 연합체로 바라봐야 한다.

C++은 여러 개의 하위 언어를 제공하며 C++의 어느 부분을 사용하느냐에 따라 programming이 달라진다.

1. C언어

2. 객체지향 개념의 C++ : class, encapsulation, inheritance, polymorphism, virtual function(Dynamic binding)

3. template C++ : 주류 C++과는 다르며 Effective C++의 item 48에서 다루고 있다.

4. STL : container, iterator, algorithm, 함수객체 등 template library를 중심으로 다룬다.

 

 

※ l-value와 r-value

l-value: 특정 메모리 위치를 가리키며 변수로 존재한다. (즉, 주소값을 취할 수 있는 연산자)

r-value: 일시적이고 주로 literal 상수이다. (주소값을 취할 수 없는 연산자)

int setValue(){ return 6; }

setValue() = 4;  // Err

setValue함수는 rvalue인 숫자 6을 반환한다.

따라서 대입연산자의 왼쪽 피연산자로 사용할 수 없기에 오류가 발생한다.

 

int global = 100;
int& setValue(){ return global; }

setValue() = 4;  // ok

여기서 setValue함수는 참조를 반환한다. 참조는 global 변수의 메모리 장소를 가리키기 때문에 lvalue로 할당할 수 있다.

 

lvalue에서 rvalue로 변환은 가능하다.

int x = 1; // l-value
int y = 2; // l-value
int z = x+y; // l-value -> r-vlaue

하지만 rvalue에서 lvalue로의 변환은 불가능하다.

 

 

 

 

 

※ 함수 오버로딩

이때, 함수 오버로딩은 C언어와 다르게 함수의 매개변수 선언형태가 다르면 동일한 이름의 함수정의 허용이 가능해 졌다.

예를 들어 매개변수가 다른것 뿐만 아니라 const선언만으로도 

int Func(int num) { return this->num; }
int Func(int num)const { return this->num; }

두 함수의 이름은 같지만 단지 const를 붙였을 뿐인데 오류가 발생하지 않는다.

 

 

 

※ 함수와 Default 값

int Func (int num = 7) {…}

int main(){
    Func()    // 7이 전달 될 것으로 간주
}

함수전달인자가 왼쪽부터 채워지기에 함수 작성시에는 오른쪽부터 Default값을 채워야 한다.

 

 

 

※ inline 함수: C의 #define 즉, 매크로 함수와 동일한 기능을 한다.

 [차이점]

  - #define: preprocessor로 compile전에 실행되며 값의 치환을 주목적으로 전처리기에 의해 실행

  - inline: 전처리가 아닌 compiler에 의한 처리과정으로 Effective C++에 의하면 주로 inline함수의 애용을 추천한다.

#define square(x) ((x)*(x))

inline int square(int x){
    return x*x;
}

위의 예시에서 두 내용은 서로 같은 의미를 내포하며 출력 시 5의 제곱은 sqaure(5)로 출력할 수 있다는 매력이 있다.

매크로 함수도 자료형 정의가 필요없다는 장점이 존재하지만 아래와 같은 치명적인 단점이 존재한다.

#define CALL_MAX(a,b) f((a) > (b) ? (a) : (b))
...
int x = 5, y = 0;

CALL_MAX(++x, y);     // a가 2번 증가
CALL_MAX(++x, y+10);  // a가 1번 증가

위의 경우, (++a>b) ? ++a : b 로 해석되기에 a가 2번 증가하게 되어 버리는 것이다.

따라서 다음과 같이 매크로의 효율은 그대로 유지하면서 타입안전성까지 취하는 inline함수에 대한 template을 사용한다.

template<typename T>
inline void callMax (const T& x, const T& y){
    f (x > y ? x : y);
}

따라서 Effective C++에서는 #define을 쓰려거든 const, enum, inline을 떠올리라는 내용을 item2에서 볼 수 있다.

 

※ [Effective C++_item 2]: #define을 쓰려면 const, enum, inline을 떠올리자 (전처리기보다 컴파일러를 가까이 하자.)

매크로는 일단 정의되면 끝날 때 까지 유효하며 컴파일러로 넘어가기 전, 전처리기가 밀어버린 후 상수로 바뀌어 버린다.

따라서 매크로는 클래스 상수 정의에도 쓸 수 없을 뿐만 아니라 캡슐화 혜택도 받지 못한다.

 

이에 대해 매크로 대신 상수를 쓰는 총 3가지의 해결책을 제시한다.

1. const 사용

#define ASPECT_RATIO 1.653 를 아래와 같이 상수로 사용하는 것이다.
const double AspectRatio = 1.653; 이는 위와 달리 사본이 사용때 마다 생기지 않고 딱 1개만 생긴다.

 

주의할 점1) 포인터와 포인터가 가리키는 대상까지 const로 선언 

const char* const name = "V2LLAIN";

주의할 점2) 클래스 멤버로 상수 정의 시, 상수의 사본개수를 1개 이하로 하고 싶을 때.

class Game {
private:
    static const int Num = 5;  //static 멤버로 만들어 상수로 선언한다.
};

 

2. enum hack을 이용한다. (const 보다는 #define에 가까운 방식, 쓸데없는 메모리 할당X)

class CostEstimate{
private:
    enum {  Num = 5  };
};

3. inline template (Effective C++_item 30)을 이용한다.

template<typename T>
inline void callMax (const T& a, const T& b) { f (a > b ? a : b); }

 

 

 

 

※ namespace:

범위지정연산자(::)를 이용해 namespace지정 시 사용하며 namespace는 변수명이 같아도 자신만의 namespace에 선언하면 충돌하지 않는다. 따라서 namespace를 중첩해서도 쓸 수 있다. (cf. ::를 변수앞에 붙이면 global variable을 의미.) 또한, std라는 namespace에 대해 using namespace std;라 선언해 std::cout을 cout이라 사용가능하다.

#include <iostream>
using namespace std;

namespace Parent{
    int num = 0;
    namespace Child{
        int num1 = 1;
        namespace GrandChild{
            int num2 = 2;
        }
    }
}

int main(){
    cout << Parent::num << endl;
    cout << Parent::Child::num1 << endl;
    
    namespace G = Parent::Child::GrandChild;
    cout << G::num2 << endl;
}

 

※ reference variables (참조변수)

참조형 변수는 Ampersand 기호, &를 사용하여 나타내는데 우리는 이런 기호를 C언어에서 사용해본 적이 있다.

바로 포인터와 관련된 내용을 배울 때 사용한 적이 있는데, 아래와 같다.

int num = 3;
int *ptr = &num;

cout << ptr;  // ptr변수로 num의 주소값인 0xc3dcbffb64 출력 
cout << *ptr; // ptr변수로 num의 값인 3 출력

이때 사용된 &는 num의 주소값을 받아온다는 의미로 사용된 &연산자이다.

 

그렇다면 참조변수의 기호 &는 무엇일까?

자신이 참조하는 변수를 대신할 수 있는 일종의 별칭으로 참조자 수에는 제한이 없다.

★배열"요소"도 변수로 간주되어 참조자 사용이 가능하다.

int arr[10];
int &ref1 = arr[1];

※배열 전체를 참조값으로 함수에 전달하기

void func (const int (&arr)[3]){
    for (int i = 0; i < 3; i++) {
        cout << arr[i];
    }
}

유의 사항: 괄호() 안에 &와 참조배열이름을 적어야 하며 [ ]안에는 반드시 배열 길이를 명시해야 한다.

 

※ 참조변수와 const

const변수를 참조하기위해서는 참조변수를 반드시 const로 선언해야 한다.

함수의 참조매개변수를 const로 선언하면 변수의 값을 변경할 수 없고 상수 또한 인자로 전달가능하다.

const int &ref = 30; //30이라는 const

 

※ Call - by - reference

참조변수로 같은 메모리 공간을 접근하면서 다른 변수의 이름으로 참조가능한 방식이다.

이로 인해 함수 밖의 메모리공간에서도 접근 및 변경이 가능하다.

변수를 매개변수로 하면  함수 호출 때 마다 메모리 할당이 발생한다.

참조변수를 매개변수로 하면  함수 호출을 해도 메모리 할당이 발생하지 않는다.

이때, 반환값은 참조형태여서 반드시 참조변수로 반환받아야 한다.

 

 

※ new와 delete

C언어의 malloc과 free를 대신해서 C++에서는 new와 delete를 사용한다.

객체 생성에서는 new를, 소멸에서는 delete를 사용하여 Heap segment에 할당, 반환을 하며 

new를 이용해 heap에 할당된 변수에 대해 참조자 선언이 가능하다.

 

이와 관련된 설명은 뒤에 객체 생성 및 소멸과 관련하여 설명하겠다.

 

 

※ Prologue _ 이름짓기(Effective C++)

포인터의 이름을 지을 때, T에 대한 포인터 (pointer to T)를 줄여서 pt라고 하는 경우가 많다.

참조자에서도 비슷하게 T에 대한 참조자 (referenece to T)를 줄여서 rf라 사용할 수 있다.

gamecharcter *pgc;
gamecharcter& rgc;

 

+ Recent posts