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;
}