Programming/C++ 복습
C++ 정리 - (1)
lee308812
2019. 10. 6. 16:08
[ Initialization ]
#include <iostream>
int main(void)
{
using namespace std;
int nValue = 5; // copy initialization
int nValue2(5); // direct initialization
int nValue3{5}; // uniform initialization
int value{4.5}; // unniform initialization(error)
return 0;
}
- 빈 {}로 uniform initialization을 하면 기본 초기화(default initialization)가 된다. 기본 초기화는 변수를 0으로 초기화한다.
- 또한, uniform initialization은 형변환을 허용하지 않는다는 이점이 있다. 변수를 다른 자료형의 값으로 초기화하려고 하면, 컴파일러에서 경고 또는 오류가 발생한다.
[ C++14 Binary Literal ]
#include <iostream>
int main(void)
{
using namespace std;
int b = 0b1010; // 2진수 표현(C++14 binary literal)
b = 0b1011'1111'1111; // 가독성을 위해 자리 끊기도 가능(다른 진수도 가능)
int o = 012; // 8진수 표현
int h = 0xA; // 16진수 표현
cout << b << endl;
cout << o << endl;
cout << h << endl;
return 0;
}
[ 함수 파라미터에서 const ]
#include <iostream>
void printNumber(const int my_number)
{
// 일반적으로, 함수에서 파라미터 값을 직접 수정하면
// 입출력을 명확하게 보여주지 못하는 측면이 있어 잘 안씀.
// 따라서 const로 막아서 값을 바꾸려면 아래와 같이 값을 n으로 복사해서 쓰도록 강제
int n = my_number;
n++;
cout << n << endl;
}
[ C++11 constexpr ]
#include <iostream>
int main(void)
{
// C++11 constexpr
// - compile time에 값이 결정되는지 확인
// - runtime에 값이 결정된다면 error
constexpr int my_const(123);
int number;
cin >> number;
constexpr int special_number = number; // error
cout << special_number << endl;
return 0;
}
[ 상수 ]
myConst.h
#pragma once
namespace constants
{
// literal 상수 = 3.14
// symbolic 상수 = const int x = 12;
constexpr double pi(3.141592);
constexpr double avogadro(6.0221413e23);
constexpr double moon_gravity(9.8 / 6.0);
}
main.cpp
#include <iostraem>
#include "myConst.h"
int main(void)
{
using namespace std;
double radius;
cin >> radius;
double circumference = 2.0 * radius * constants::pi;
return 0;
}
[ Comma Operator ]
#include <iostream>
int main(void)
{
using namespace std;
// comma operator
// : 앞의 것을 계산하고 뒤에 것을 계산하고 뒤에 것이 대입됨.
int x = 3;
int y = 10;
int z = (++x, ++y); // 값은 11.
// ++x; ++y; int z = y를 풀어 쓴거임. for문에서 종종 쓰임
cout << x << " " << y << " " << z << endl;
return 0;
}
#include <iostream>
int main(void)
{
using namespace std;
int a = 1, b = 10;
int z;
z = a, b; // z는 1이 됨.
// , 연산자는 우선순위가 =보다 낮다. 따라서 먼저 대입됨.
// (z = a), b; 와 동치
cout << z << endl;
return 0;
}
[ 부동소수점에서 관계연산자 사용할 때 주의 ]
#include <iostream>
#include <cmath>
int main(void)
{
using namespace std;
double d1(100 - 99.99); // 0.001
double d2(10 - 9.99); // 0.001
cout << d1 << endl;
cout << d2 << endl;
cout << d1 - d2 << endl; // 아주 작은 값이 나옴..
if (d1 == d2)
cout << "Equal" << endl;
else
cout << "Not Equal" << endl; // 출력됨
// 같은 값으로 취급하려면
const double epsilon = 1e-14;
if (std::abs(d1 - d2) < epsilon)
cout << "Approximately Equal" << endl;
else
cout << "Not Equal" << endl;
return 0;
}
[ Bitset ]
#include <iostream>
#include <bitset>
int main(void)
{
using namespace std;
unsigned int a = 1;
cout << std::bitset<4>(a) << endl; // 0001
return 0;
}
[ Bit Flag ]
#include <iostream>
#include <bitset>
int main(void)
{
using namespace std;
// bitmask. array를 좀더 compact하게 쓴다는 개념?
const unsigned char opt0 = 1 << 0;
const unsigned char opt1 = 1 << 1;
const unsigned char opt2 = 1 << 2;
const unsigned char opt3 = 1 << 3;
unsigned char items_flag = 0;
cout << bitset<8>(opt0) << endl;
cout << bitset<8>(opt1) << endl;
cout << bitset<8>(opt2) << endl;
cout << bitset<8>(opt3) << endl;
cout << "No item : " << bitset<8>(items_flag) << endl;
// item0 on
items_flag |= opt0;
cout << "Item0 obtained : " << bitset<8>(items_flag) << endl;
// item3 on
items_flag |= opt3;
cout << "Item3 obtained : " << bitset<8>(items_flag) << endl;
// item3 lost
items_flag &= ~opt3;
cout << "Item3 lost : " << bitset<8>(items_flag) << endl;
// item1 check?
if (items_flag & opt1) cout << "Has item1" << endl;
else cout << "Not have item1" << endl;
// item0 check
if (items_flag & opt0) cout << "Has item0" << endl;
else cout << "Not have item0" << endl;
// obtain item 2, 3
items_flag |= (opt2 | opt3);
cout << "Item2, 3 obtained : " << bitset<8>(items_flag) << endl;
if ((items_flag & opt2) && !(items_flag & opt1))
{
items_flag ^= opt2; // items2 obtained -> lost
items_flag ^= opt1; // no items1 -> obtained;
}
cout << bitset<8>(items_flag) << endl;
// 실제 사용 예)
// [ openGL ] - 옵션이 되게 많은데, 비트마스크로 파라미터 수를 간소화 가능
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// glClear(GL_COLOR_BUFFER_BIT);
return 0;
}
[ Bit mask ]
#include <iostream>
#include <bitset>
const unsigned int red_mask = 0xFF0000;
const unsigned int green_mask = 0x00FF00;
const unsigned int blue_mask = 0x0000FF;
int main(void)
{
using namespace std;
unsigned int pixel_color = 0xDAA520;
cout << std::bitset<32>(pixel_color) << endl;
unsigned char red, green, blue;
red = (pixel_color & red_mask) >> 16;
green = (pixel_color & green_mask) >> 8;
blue = (pixel_color & blue_mask) >> 0;
cout << hex; // 16진수 표시(8진수는 oct)
cout.setf(ios::showbase); // 0x, 0 등 접두어 표시
cout.setf(ios::uppercase); // 대문자로 표시
cout << "red : " << (unsigned int)red << endl;
cout << "green : " << (unsigned int)green << endl;
cout << "blue : " << (unsigned int)blue << endl;
cout << dec; // 다시 10진수로
cout << 15 << endl;
return 0;
}
[ Global Variable 사용 ]
#include <iostream>
using namespace std;
int value = 123;
int main(void)
{
cout << value << endl;
int value = 1; // local variable
cout << value << endl;
cout << ::value << endl; // global variable 사용
return 0;
}
[ static variable ]
#include <iostream>
using namespace std;
void doSomething()
{
int a = 1;
++a;
cout << a << endl;
}
void doSomethingStatic()
{
// OS로부터 받은 메모리가 static임.
// = 메모리가 정적으로 선언이 됐다.
// 같은 메모리를 쓰고, 초기화를 한번밖에 하지 않는다.
static int a = 1;
++a;
cout << a << endl;
}
int globalA = 1;
void doSomethingGlobal()
{
// - static과 같은 기능을 전역변수에서도 사용할 수 있다.
// - 단, static은 해당 scope에서만 접근가능.
// - static의 경우 처음 구문이 시행되는 시점에 생성자를 호출하도록 하여,
// 생성자의 초기화 시점을 조절할 수 있다.
++globalA;
cout << globalA << endl;
}
int main(void)
{
doSomething(); // 2
doSomething(); // 2
doSomethingStatic(); // 2
doSomethingStatic(); // 3
doSomethingGlobal(); // 2
doSomethingGlobal(); // 3
return 0;
}
[ Extern Linkage / Interal Linkage ]
myConstant.h
namespace Constants
{
const double PI(3.141592);
const double gravity(9.8);
// ...
}
test.cpp
#include <iostream>
#include "myConstant.h"
extern int z = 456;
void doSomething()
{
using namespace std;
cout << "In test.cpp : " << Constants::PI << " " << &Constants::PI << endl;
}
main.cpp
#include <iostream>
#include "myConstant.h"
using namespace std;
// === external linkage ===
// 동일 cpp파일 내에서 g_a를 자유롭게 쓸 수 있다.
// 딴데서 extern 해버리면 딴데서 쓸 수 있다.
int g_a = 9;
// 동일 cpp 파일 내에서 g_staticA를 자유롭게 쓸 수 있다.
// extern static은 불가능. static을 붙이면 다른 파일에서 사용하는 것도 불가능.
static int g_staticA = 7;
// === external linkage ===
// 전역 변수를 다른 file에서 사용할 수 있다.
void doSomething(); // 다른 어떤 cpp에 body가 있다. 실제로는 extern이 생략되어있다.
// ★ 만약 다른 cpp에서도 extern int z 선언 및 초기화를 하지 않으면
// 메모리가 할당 안되어 못찾음.
// ★ 여러 군데에서 extern int z = 1;로 할당을 하면 중복이라서 에러가 뜬다.
// 전역 변수가 위험한 이유 중 하나.
extern int z;
int main(void)
{
cout << z << endl;
cout << "In main.cpp file : " << Constants::PI << " " << &Constants::PI << endl;
doSomething(); // 주소가 다르게 나온다. 여러개 만들면 그만큼 메모리 낭비.
// 해결방법
// (1) cpp파일을 하나 더만든다. (myConstant.cpp)
// (2) 거기다가, extern을 사용해서 아래와 같이 선언.
/*
namespace Constants
{
extern const double PI(3.141592);
extern const double gravity(9.8);
// ...
}
// (3) 그리고 헤더 파일에서는 초기화를 하지 않고, extern 키워드만 추가해준다.
*/
return 0;
}
[ auto 키워드 ]
#include <iostream>
auto add(int x, int y)
{
return x + y;
}
auto doubleAdd(int x, int y)
{
return x + (double)y;
}
// 매개 변수에는 auto를 쓸 수 없다. template을 사용해야 함.
// auto는 타입 추론의 목적이고,
// 아래 경우는 여러개의 함수를 오버로딩 하는 목적임.
/*
auto addError(auto x, auto y)
{
// ...
}
*/
// Trailing return type
auto addTrailing(int x, int y) -> int
{
return x + y;
}
// 3.14의 타입을 추론하여 auto의 타입을 결정
auto getPI() -> decltype(3.14)
{
return 3.141592;
}
// 아래의 경우에서 의도하지 않은 타입이 결정될 수 있다.
// addBad(3.14, 'A')
// addBad("X", 'y')
template <typename one, typename two>
auto addBad(one first, two second)
{
return first + second;
}
// 따라서 리턴 타입을 auto로 할꺼면, 아래 둘 중 하나의 방법을 사용해야 한다.
template <typename one, typename two>
auto addGoodOne(one first, two second) -> decltype(first + second)
{
return first + second;
}
template <typename one, typename two>
decltype(auto) addGoodTwo(one first, two second)
{
return first + second;
}
int main(void)
{
using namespace std;
auto a = 1 + 2.0f; // float
auto result = add(1, 2); // int
auto resultD = doubleAdd(1, 2); // double
return 0;
}
[ typeInfo ]
#include <iostream>
#include <typeinfo>
int main(void)
{
using namespace std;
// 어떤 타입인지 출력을 해준다.
cout << typeid(4.0).name() << endl;
int a = 3;
cout << typeid(a).name() << endl;
return 0;
}
[ setprecision ]
#include <iostream>
#include <iomanip>
int main(void)
{
using namespace std;
double d = 0.123456789;
float f = d; // loss
cout << std::setprecision(12) << f << endl;
return 0;
}
[ std::string 관련 ]
#include <iostream>
#include <string>
#include <limits>
int main(void)
{
using namespace std;
cout << "Your Age : ";
int age;
cin >> age;
// \n이 올 때까지 streamsize의 최대값 개수만큼 글자를 버퍼에서 지워라.
std::cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "Your Name : ";
string name;
std::getline(std::cin, name); // line 전체 입력 받음
// \n가 id로 전달되지 않도록 하기 위함.
cout << "Your ID : ";
string id;
std::getline(std::cin, id);
return 0;
}