입출력 함수

  • C 언어

#include <stdio.h>

int main() {
    int nbr = 1;
    char sentence[1024];
    print("Enter some characters: ");
    scanf("%s", sentence);
    printf("%d: %s\n", nbr, sentence);
    printf("Hello World!\n");
    return 0;
}
  • C++

#include <iostream>

int main() {
    int nbr = 1;
    char sentence[1024];
    std::cin >> sentence;
    std::cout << x << ": " << sentence << std::endl;
    std::cout << "Hello World!" << std::endl;
    return 0;
}
  • 헤더 파일의 변화: <stdio.h> 에서 <iostream>

  • C언어에서 사용된 헤더파일은 대부분 앞에 c를 붙이고 ".h"를 제외하면 똑같이 사용 가능.

    • <stdio.h> == <cstdio> 또는 <stdlib.h> == <cstdlib> 또는 <string.h> == <cstring> 등

  • C++에서 표준 입출력 함수는 입력이나 출력 형식을 지정하지 않고 사용함. 소수점 자릿수 등을 따져야할 경우 C언어의 표준 입출력을 가져다 써야함.

  • 속도는 C언어의 표준 입출력(printf/scanf)가 C++ 것(cin/cout)보다 빠름.

  • C++ 에서 개행문자는 std::endl 로도 표현 가능.

 

bool type

  • C언어에서는 조건문의 참, 거짓을 표현하기 위해 int 타입을 사용.

    • 참: 0을 제외한 모든 값

    • 거짓: 0

#include <stdio.h>

int main() {
    int x = 1;
    
    if (x) {
        printf("x is true.\n");
    } else {
        printf("x is false.\n");
    }
    return 0;
}
  • C++에서는 참과 거짓을 명확하게 사용하기 위해 bool 타입이 추가됨.

    • 참: true

    • 거짓: false

#include <iostream>

int main() {
    bool b = true;
    
    if (b) {
        std::cout << "b is true." << std::endl;
    } else {
        std::cout << "b is false." << std::endl;
    }
    
    // 표준 출력 시 int 타입
    std::cout << b << std::endl; // 1
    std::cout << !b << std::endl; // 0
    
    // bool 포맷을 유지하기
    std::cout.setf(std::ios::boolalpha);
    std::cout << b << std::endl; // true
    std::cout << !b << std::endl; // false;
    
    // 다시 int로 출력하기
    std::cout.unsetf(std::ios::boolalpha);
    std::cout << b << std::endl; // 1
    std::cout << !b << std::endl; // 0
    
    return 0;
}

 

auto keyword

  • C++11 부터 추가된 auto 타입 연산자를 사용하면, 컴파일 타임에 타입을 추론해 어떤 타입인지 결정함.

  • 기본 내장 타입을 포함해 컴파일 타임에 추론 가능한 모든 타입에 사용 가능.

  • 만약 컴파일 타임에 추론이 불가능하다면, 오류가 발생함.

#include <iostream>
#include <vector>

int main() {
    int i1 = 10;
    double d1 = 3.14;
    
    auto i2 = 10; // integer
    auto d2 = 3.14; // double
    
    // 복잡한 타입을 가지는 경우
    std::vector<std::pair<int, double> > vPairs;
    for (std::vector<std::pair<int, double> >::iterator iter = vPairs.begin();
        iter != vPairs.end(); ++iter)
    {
        std::cout << "So complex!" << std::endl;
    }
    
    // auto keyword
    for (auto iter = vPairs.begin(); iter != vPairs.end(); iter++)
    {
    	std::cout << "auto keyword is good." << std::endl;
    }
    
    return 0;
}

 

범위 기반 for문(Range-based for)

  • for 문은 지정된 횟수만큼 반복하는 작업에도 사용하나, 배열이나 각종 컨테이너에 있는 각 요소들에 무언가를 수행하기 위해 순회하는 목적으로도 많이 사용함.

  • 그러나, 조건문에 배열이나 컨테이너에 몇 개의 요소가 들어있는지 명시해야 한다는 단점이 있음.

  • 범위 기반 for문은 요소들의 개수에 상관없이 반복문을 수행함.

#include <iostream>

auto mean(const Sequence& seq)
{
    auto n = 0.0;
    for (auto& x : seq) {
        n += x;
    }
    
    return n / seq.size();
}

int main() {
    int arr[] = { 1, 2, 3, 4, 5 };
    for (auto& i : arr) {
        std::cout << i << std::endl;
    }
    
    // same as
    for (int i = 0; i < 5; ++i) {
        std::cout << i << std::endl;
    }
    return 0;
}

 

메모리 할당과 해제

  • C언어

#include <stdio.h>
#include <stdlib.h>

int main() {
    int i;
    int** arr = (int**) malloc(sizeof(int*) * 5);
    for (i = 0; i < 5; ++i) {
        arr[i] = (int*) malloc(sizeof(int) * 5);
    }
    
    for (i = 0; i < 5; ++i) {
        free(arr[i]);
    }
    free(arr);
    arr = NULL;
    
    return 0;
}
  • C++

#include <iostream>

int main() {
    int** arr = new int*[5];
    for (int i = 0; i < 5; ++i) {
        arr[i] = new int[5];
    }
    
    for (int i = 0; i < 5; ++i) {
        delete[] arr[i];
    }
    delete[] arr;
    arr = NULL;
    
    int* p1 = new int;
    delete p1;
    p1 = NULL;
    
    int* p2 = new int[10];
    delete[] p2;
    p2 = NULL;
    
    return 0;
}

 

널 포인터

  • C언어에서 널 포인터를 나타내기 위해 NULL이나 상수 0을 사용.

    • #define NULL 0

    • 따라서 NULL == 0

  • NULL 은 포인터가 아니므로, 함수에 인자로 넘기는 경우 int 타입으로 추론되는 문제가 발생함.

  • C++에서는 nullptr 키워드로 널 포인터를 표현함.

  • nullptr은 널 포인터 리터럴(Literal) 이며, 실제 타입은 std::nullptr_t 이다.

#include <iostream>

// function overloading
void fn(int a) {
    std::cout << "fn(int)" << std::endl;
}

void fn(int* a) {
    std::cout << "fn(int*)" << std::endl;
}

int main() {
    fn(0); // fn(int)
    fn(NULL); // fn(int)
    fn(nullptr); // fn(int*)
    
    std::cout << sizeof(nullptr) << std::endl; // 4
    std::cout << typeid(nullptr) << std::endl; // std::nullptr_t
    
    int* p1 = new int;
    delete p1;
    p1 = nullptr; // DO NOT USE NULL
    
    int* p2 = new int[10];
    delete[] p2;
    p2 = nullptr; // DO NOT USE NULL
    
    return 0;
}

 

명시적 캐스팅

  • C언어에서는 int(..) 또는 char(...)를 통해 명시적(explicit) 캐스팅을 할 수 있으나, 개발자의 실수를 그대로 용납하기 때문에 차후에 오류가 생길 수 있음.

  • C++에서는 보다 명시적인 캐스팅 방법으로 캐스팅의 목적을 명확하게 명시함으로써 개발자의 의도를 컴파일러에게 전달하여 오류를 방지함.

  • static_cast<타입>(표현식);

#include <iostream>

int main() {
    char* str = "Hello, World!";
    int* intPtr = static_cast<int*>(str);
    char* charPtr = static_cast<char*>(*intPtr);
    
    std::cout << charPtr << std::endl;
    
    /* In the case of C
       int* intPtr = (int*) str;
       char* charPtr = (char*)*pi;
     */
    
    return 0;
}

 

범위 지정 열거체(Scoped enum)

  • C/C++ 에서 기존에 사용하던 열거형은 전역(global) 범위로 선언되기 때문에 A 열거체에서 사용한 T라는 이름은 B 열거체에서는 사용할 수 없음.

  • 또한, 기본적으로 열거체의 타입은 int이기 때문에 묵시적으로 변환되어 서로 다른 열거체 변수끼리 더하거나 비교할 수 있음.

#include <stdio.h>

/* C/C++98 */
int main() {
    enum TrafficLight { Red, Yellow, Green };
    enum Job { Warrior, Ranger, Wizard };
    enum Coffee : unsigned char { Latte = 10, Mocha = 25 };
    
    int jobNum = Warrior;
    int i = Green + Latte; // 12
    
    if (Yellow == Ranger) {
        printf("Same!\n");
    } else {
        printf("Different!\n");
    }
    
    return 0;
}
  • C++11부터 위의 문제점들을 보완하기 위해 범위 지정 열거체를 제공.
    • enum 대신 enum class를 사용.
    • 묵시적 변환이 되지 않으므로, 명시적으로 타입을 지정하지 않으면 오류 발생.
    • 서로 다른 열거체 변수끼리 연산할 수 없음.
#include <iostream>

// C++11
int main() {
    enum class TrafficLight { Red, Yellow, Green };
    enum class Job { Warrior, Ranger, Wizard, Green };
    enum class Coffee : unsigned char { Latte = 10, Mocha = 25 };
    
    Job jobNum = Job::Warrior;
    int i = static_cast<int>(TrafficLight::Green)
        + static_cast<int>(Coffee::Latte);
    
    // TrafficLight::Yellow != Job::Ranger
    
    return 0;
}

 

Binary literal, separator

  • C/C++에서는 10진수 외에 8진수, 16진수를 표현할 수 있음.

    • 8진수: 061 (=49) --> 0으로 시작

    • 16진수: 0x3A (=58) --> 0x로 시작

  • C++14부터 2진수를 표현하는 방법이 추가됨. 0b로 시작 --> 0b110101

  • C++14부터 구분자(separator)가 추가되어 큰 숫자를 읽기 쉬움.

    • int INT_MAX = 21'4748'3647

#include <iostream>

int main() {
    int decimal = 52;
    int octal = 064;
    int hexadecimal = 0x34;
    int binary = 0b110100; // C++14
    
    int maxInt_Cpp98 = 2147483647;
    int maxInt_Cpp14 = 21'4748'3647; // C++14
    
    return 0;
}

 

문자열(string) 자료구조

  • C언어에서는 문자열을 나타내기 위해 char[] 또는 char* 를 사용하였으나, 이는 비교(strcmp), 연결(strcat), 길이(strlen) 등 문자열 관련 함수를 사용할 때 불편함.

#include <stdio.h>

int main() {
    char str1[30] = "Hello, World!";
    char* str2 = "Hello, ke2ek!";
    
    if (strcmp(str1, str2) == 0) printf("Same!\n");
    else printf("Different!\n");
    
    strcat(str1, str2);
    printf("%s\n", str1); // Hello, World!Hello, ke2ek!
    printf("%d, %d\n", strlen(str1), strlen(str2));
    
    return 0;
}
#include <iostream>
#include <string>

int main() {
    std::string str1 = "Hello, World!";
    std::string str2 = "Hello, ke2ek!";
    
    if (str1 == str2) std::cout << "Same!" << std::endl;
    else std::cout << "Different!" << std::endl;
    
    str1 = str1 + str2;
    std::cout << str1 << std::endl; // Hello, World!Hello, ke2ek!
    std::cout << str1.size() << ", " << str2.length() << std::endl;
    
    return 0;
}

 

레퍼런스(Reference) 변수

  • 다른 변수를 가리키는 변수 (포인터 기능의 일부)로, 다른 변수와 동일한 메모리 위치를 공유함.

  • 레퍼런스 변수로 연산할 때는 포인터와 달리 *나 &를 추가해야 하는 문법이 없음.

  • 레퍼런스는 항상 유효한 메모리 주소를 참조해야 하므로, const처럼 선언과 동시에 초기화를 해주어야 함.

#include <iostream>

// Pass-by-pointer
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// Pass-by-reference
void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

void print(int a, int b) {
    std::cout << "a = " << a;
    std::cout << ", b = " << b << std::endl;
}

int main() {
    // int& ref; NEED TO ASSIGN.
    int i = 10;
    int &ret = i;
    
    std::cout << "ref = " << ref << std::endl; // 10
    
    ret += 10;
    std::cout << "i = " << i << std::endl; // 20
    std::cout << "ref = " << ref << std::endl; // 20
    
    int a = 10, b = 20;
    print(a, b); // a = 10, b = 20
    swap(&a, &b); // Pass-by-pointer
    print(a, b); // a = 20, b = 10
    
    int c = 30, d = 40;
    print(c, d); // c = 30, d = 40
    swap(c, d); // Pass-by-reference
    print(c, d); // c = 40, d = 30
    
    return 0;
}

 

함수 오버로딩

  • C언어와 달리, C++에서는 여러 함수를 동일한 이름으로 선언할 수 있는 함수 오버로딩이라는 개념을 지원함.

  • 함수 오버로딩 규칙

    • 오버로딩된 함수는 인수의 개수가 서로 달라야 함.

    • 인수의 개수가 같은 경우, 인수의 타입이 서로 달라야 함.

    • 리턴 타입이 다른 경우, 같은 이름의 함수를 사용할 수 없음.

#include <iostream>

int sum(int num1, int num2);
int sum(int num1, int num2, int num3);
int sum(short num1, short num2);

// Compile Error!
// short sum(short num1, short num2);

int main() {
    int a = 10, b = 20, c = 30;
    int d = sum(a, b);
    int e = sum(a, b, c);
    short f = 40, g = 50;
    int h = sum(f, g);
    return 0;
}

 

네임스페이스(Namespace)

  • 어떤 프로젝트에서 A와 B가 구현한 코드를 합치는데 동일한 이름의 변수나 함수가 있을 경우, 기본 타입을 확장해서 구분해줄 필요가 있음. C언어에서는 이런 문제를 해결할 수 없으나, C++에서는 네임스페이스를 이용해서 해결 가능.

  • 네임스페이스는 namespace 이름 { ... } 으로 정의하며, 중첩해서 사용 가능.

  • 네임스페이스 A에 있는 변수 a에 접근하려면, 범위 지정 연산자(::)를 사용해야 함. A::a

  • 표준 라이브러리의 네임스페이스는 std로, cin/cout 같은 표준 입출력 사용 시 std::cin 또는 std::cout으로 접근해야 함.

  • 네임스페이스를 매번 적기 귀찮을 경우, using namespace 이름; 을 헤더파일 밑에 선언해주면 됨.

    • 표준 라이브러리 사용 시, using namespace std; 를 많이 사용함.

#include <iostream>
using namespace std;

namespace A {
    void print() {
        cout << "A's print()" << endl;
    }
}

namespace B {
    void print() {
        cout << "B's print()" << endl;
    }
}

int main() {
    A::print(); // A's print()
    B::print(); // B's print()
    
    return 0;
}

 

 

+ Recent posts