2021년 6월 1일 화요일

Cpp 잡지식

잡지식

    포함요소

        스파게티 코드 -> 함수를 포함한 C 의 절차적 언어 

        -> C++ 초기의 OOP -> Template 과 같은 Generic Programming. 

    표준

        ISO C++ 에서 지정함. 

        C++98/C++03 (03은 오탈자, 모호한 문구 수정 정도로 변동이 없음)

        C++11(글 쓰는 시점 가장 많이 씀)

    용어

        Declaration/Definition

            선언이 위에 Prototype(원형)만 적는 거고 정의가 내용까지 포함하는 거임

        Function/Procedure

            리턴값이 있으면 Function 없으면 Procedure. 대가 Function 으로 통용됨.

        Access Specifier / Qualifier

            전자는 클래스에 쓰는 public 같은거. 접근 제한자라고 함

            후자는 const 같은거. 제한자라고 함.

    Define 대신 Const 쓰면 좋은 이유

        1. 자료형 명시적임.

        2. Namespace 사용 가능.

        3. 배열도 가능 ( C++ 만 되고 C 는 안댐)

    논리연산자

        iso646.h 쓰면 and, or 등이 예약어(특, Define)됨

        cctype 쓰면 문자열 걸러짐. 

            ex) ispunct()-구둣점, isalpha()-알파벳, isspace - whitespace

        ternary conditional 이랑 if 문이랑 속도비교 물어보면 그런거 신경쓰지 말라함(화냄)


헷갈리는거

    Const 

        const A* 는 지시 값을 고정. A* const 는 포인터 값을 고정.

            함수에 파라미터에 const 붙인다고 하면 전자만 필요하지 후잔 ㄴㄴ

        const 포인터끼리는 섞이면 안됨.

            const A* a;  A** b = &a; 이거 안됨. 

        const 를 붙인 reference 만 l value를 받을 수 있는 이유 

            함수 내부에서 l value 의 reference 를 고칠 경우 전혀 의미가 없음

            이런 경우를 막기 위해서 컴파일 차원에서 막음. 

    Function

        float (*pt)(int); 를 사용한다고 하자. pt(10), (*pt)(10) 모두 옳다.

        함수포인터 배열

            float (*pt)(int); 형식 => float (*pt[10])(int);

    Uniform Initialize

        배열 초기화 시 = 필요없음 ( float a[2] {1, 2}; )

        자료형 자동 변환 안됨.

        private 에도 멤버변수의 정의 순서에 따라 자동으로 매핑됨

        상속 받으면 따로 지정 안하는 이상 안댐. -> std::initializer_list

    Class Initialize List

        list 에 적은 순이 아니라 정의된 순으로 됨

        In Class Initialize 를 무효화 함. (둘다 실행이 아님)

        오버로딩된 생성자를 여기다 실행하면 오류없이 사용가능

    RTTI(Runtime Type Identification)

        dynamic_cast

            pointer 면 실패시 nullptr, reference 면 실패시 bad_cast 예외.

        type_id

            type_info 객체를 리턴함. 

        가상함수에서만 사용가능함.

        프로젝트 옵션에서 키고 끌 수 있음(구버전 지원용)

    reintepret_cast 제한

        pointer 형을 그걸 담을 수 없는 정수형으로 못감

        funciton pointer <-> data pointer 안댐

    Unique_Ptr

        스택에 있거나 임시객체를 리턴하는 경우 복사본을 리턴해도 됨. 

        ex) return make_unique<int>();

        마찬가지로 임시 객체의 경우 shared_ptr 에 직접 대입도 됨

    가변인자 템플릿

        단항우측폴드는 c++17 부터 가능

template<typename ...Args>
void Print(const Args& ...args)
{
	cout << "size is " << sizeof...(args) << endl;
	((cout << args << ' '), ...); // 단항 우측 폴드 적용
}

void Print2(const char* str...)
{
	va_list ap;    // cstdarg
	char buff[100];
	//va_arg(ap, num); // 몇번째의 가변인수 받기
	va_start(ap, str);  //
	vsnprintf(buff, sizeof(buff), str, ap);
	va_end(ap);

	std::cout << buff << std::endl;
};

int main()
{
	Print(1, 2, 3, 4, 5);
	Print2("%d %d %d %d %d", 1, 2, 3, 4, 5);
}

        


Main

    컴파일러가 프로그램에 추가하는 시동코드에 의해 운영체제에서 호출됨.

    클래식 C 에서는 리턴값 정의 없이 main() {..} 이렇게 쓰기도 한다.

    표준은 리턴값은 void 가 아니라 int 임을 주의. (전자는 표준이 아니라 작동안할수도)

    main 에 한정해서 return 값을 생략해도 됨(return 0; 이 자동을 붙음).


Compile/Memory

    세미콜론

        다른 언어는 구문 분리자(Statement Separator) 를 사용함. 

        Cpp 은 문장의 Terminator 로서 ; 를 사용함. 

        So Former can omit Statement Separator but later cannot.

        하지만 그 덕분에 한줄에 여러 명령어도 넣을 수 있다.

    Name Decoaration/Mangling

        함수를 컴파일하면 return type, parameter type 에 따라 함수이름에 붙는 것.

        컴파일러마다 붙이는 방법(어떤 암호화냐 등)이 다름.(Language Linkage)

        그래서 컴파일러가 다르면 서로 링크가 안되는 경우가 많음.

        C,C++ 은 호환되므로 어떤 프로토콜로 Mangling 할지 지정할 수 있음

            extern "C" void AAA(int);    extern "C++" void AAA(int);

            extern 꼭 붙어야함. (함수는 기본이 External Linkage 이긴 함)

            그래서 위 예의 두번째는 "C++" 이랑 extern 없애도 같은말임.

    데이터 존속 유형

        Automatic Storage Duration

            지역변수 - Link 는 일어나지 않고 Block 내에서만("{}") 존속

            auto 는 예전에 자동변수 나타내려고 쓰임(당연히 지금도 전역변수론 안됨)

            register 변수

                앞에 register 를 붙여서 메모리가 아니라 레지에 변수를 넣어 빠름을 꾀함.

                지금은 그 기능은 사라지고 과거코드 호환성땜에 남음

        Static Storage Duration

            Internal/External/No Link 의 3가지 버전의 변수가 가능.

            Internal 은 nameless namespace 에 쓰는 걸 권장.

            external 은 namespace 에 써서 extern 안쓰는 걸 권장함.

int global = 1000;          // external link. header 에서 extern 으로 선언해서 공유가능
static int inner = 10;      // external link

int main()
{
    static int no_link = 0; // No Link
}

            Zero Initialization

                컴파일러가 파일 실행 전(컴파일타임)에서 자동으로 0으로 초기화해줌. 

                만약 다른 상수가 대입되어 있다면 후에 Compile 단계에서 초기화해줌.

                만약 외부 함수를 쓰는 등 컴파일 때 불가능이면 프로그램 시작시에 초기화됨.

            참조 선언

                extern. 초기화되는게 아니라 함수처럼 선언만 하는거임.

            Scope Resolution Operator ::

                :: 만 붙이면 전역 변수를 가져오게 됨.

            inline

                Internal Link 로 강제. 헤더에 구현부가 있어야하는 이유를 생각하면 당연

        Thread Storage Duration

            thread_local 를 붙여서 Thread 마다 변수 가지게 가능

        Dynamic Storage Duration

            new 로 Free Storage 에 저장되고 delete 까지 존속하는 시간

    제한자

        static 

            전역에서 쓰면 내부 링크 변수 및 함수로 만들어줌.

            함수는 기본이 extern 선언됨을 고려.

        const

            값 못바꿈 + 내부링크로 바꿔줌.

            만약 외부링크를 걸고 싶으면 extern static ... 으로 명시적으로 정의해야함.

                이렇게 정의한 변수는 외부파일에서 extern 으로 선언 후 쓸 수 있음.

        volatile

            외부 장치의 주소 등은 프로그램 외부에서 바뀔 수 있는 값임.

            이런 값을 프로그램 최적화 때문에 레지스터에 넣으면 비효율적임.

            그래서 레지스터에 넣지 말라고 붙이는 거.

            const_cast 로 붙이고 뗄 수 있음.

        mutable

            const 로 제한된 구조체의 멤버가 mutable 이 붙으면 이건 바꾸게 허용해줌.

    Name Space

        Transitive 가 성립함

            AAA::BBB::aaa; 가 있을 때

            using namespace AAA; using namespace BBB; 로 aaa 가져올 수 있음.

            그리고 namespace 안에서 using namespace 가 있으면 그거도 가져옴.

        이름공간 생략 시

            Internal Link 공간이 되어 static 의 대체제임. :: 로도 가능.

        Static Value Link 용

            extern 안쓰고 namespace 에 넣기.

            static 안쓰고 nameless namespace 에 넣기


Literal

    Width

        int 의 크기는 OS 가 가장 효율적으로 사용하는 크기었음(16bit, 32bit)

        근데 64bit 의 경우 4bit 임

    상수의 기본 자료형

        표현 범위에 따라 int, long, long long 중 커버되는 가장 작은 자료형 사용.

    접두사 접미사

        정수에 대한 접미어로는 u, ul, ull, f 등이 있음.

        소수에 대한 접미어로는 f, l 이 있음.

        문자

            u"asdf" 가 char16_t, U"asfd" 가 char32_t, L"asdf" 가 wchar_t, 

            R"(asdf)" 는 raw 문자열로 escape 문자 및 엔터 등이 그대로 적용 

            이때 " 과 ( 사이에 어떤 문자가 들어가도 되고 끝에도 대칭되게 적용해야함.

    Escape Sequence

        Numeric Escape Sequence

            escape sequence 는 대응되는 코드가 있다.

            Ascii 에서 \n 은 10 에 해당되듯 말이다.

            그래서 \012 (8진수), \x0a (16진수) 로 적어도 되고 이걸 소제목이라 함.

        Universal Name Code

            IOS10646 국제표준에 맞는 유니코드를 \u+[16진수] 로 표기 (ex. \u0F16)

            단 IOS 10646 를 지원하지 않는 c++ 의 경우 원하는 글자 안나옴.

            인코딩이므로 시스템에 맞는 인코딩에 따라 표기문자는 달라짐.

    부동소수점

        IEEE754 32bit 소수 표기

            32bit 의 경우 부호부(1) + 지수부(8) + 가수부(23)

            부호부 => 양수는 0 음수는 1

            지수부 => 0111111(127) 이 0승이고 밑으로 가면 -제곱 위로가면 +제곱

            -13.625 => -1101.101 => -1.101101*2^3 

                => 1 + 101101 + 10000002 => 0xC15A00

            가수부가 나타내는 숫자의 수를 유효숫자라고 하며 소숫점 연산 에러의 원인

        limit.h

            FLT_DIG => 최소보장 유효숫자. 0.333333 에서 100 곱하니 33.333300 이면 6개임.

            FLT_MANT_DIG(가수표현비트), FLT_MAX_10_EXP

    타입 변환

        계산시 자동으로 변환

            short 처럼 int 형보다 작은 자료형을 사용하는 경우, 정수로 변환해서 계산.

            이는 컴퓨터가 기본적으로 다루는 크기가 int 형의 크기이기 때문임.

            정수와 실수형 사이의 계산에서도 더 큰 범위를 나타는 걸로 바뀜. (int->float)

        승급/일반 변환            

            정수형과 실수형과 bool형과 같이 Literal 에도 종류가 있음.

            같은 종류 내에서 변환이 전자, 다른 종류 간에선 후자라고 함.

    String Literal

         Const Char (문자열 상수)

             읽기 전용 공간에 따로 저장됨 ( 힙, 스택 아님 )

         White Space 로 연결하면 자동으로 합쳐짐

             cout < "asfd" "sadf"; 가능

    

간단한 데이터구조

    Struct

        Bit Field

            멤버 옆에 : 붙이고 숫자 적으면 그 숫자의 bit만큼 할당됨

            Bit 단위라고 해도 int 크기 단위로 메모리를 처리하게 됨.

    Union

        같은 메모리를 공유하지만 자료형만 바꾸고 싶을 때 사용.

        정의된 멤버중 최대크기의 메모리만큼 할당됨.

        특히 anonymous Union 이 유용함.

struct MyData
{
    int a : 1;
    union
    {
        long id;
        bool id_bool;
    } b;   // MyData.b.[id, id_bool]
    union
    {
        long id;
        char id_char; // MyData.[id, id_char]
    };
};

    Enum

        Enum -> 정수형 은 가능한데 역방향은 보통 컴파일 안해줌.

        커버되는 범위는 최대/최솟값을 커버하는 2의 제곱(음수면 -붙임) 에서 -1 을 해줌.

        자료형을 명시하지 않으면 커버되는 범위에 따라 컴파일러가 메모리 크기를 정함.

        1바이트 Enum 도 가능하다는 소리.

    Pointer

        int* a, b; 이렇게 하면 b 는 int 형임.

        int* a, *b; 이렇게 해야 b 도 int* 형임.

        포인터에 유일하게 대입 가능한 정수는 0 뿐임.

            NULL 이 #define 0 으로 정수인데 nullptr 의 0x00000000 로 언젠가 대체?

            new, malloc 등으로 할당했을때 실패시 nullptr 이 리턴됨.

                최근엔 std::bad_alloc 을 자동으로 반환함

                new(std::nothrow) int[10]; 이렇게 예외 안뜨게도 가능

        포인터와 정수를 계산하면 리턴값은 당연히 포인터임.

            이때 1은 포인터가 된 자료형의 바이트 크기로 변환되서 값이 바뀜

        배열과 구성은 똑같지만 다름

            배열은 sizeof 하면 배열의 크기가 나오고

            +- 연산도 안됨  

            ranged for loop 이 됨

            함수 파라미터에 크기를 명시적으로 지정할 수도 있지만 포인터랑 똑같음.

                대신에 함수 인터페이스에 상수가 있으니 이 값을 쓰면 됨 

        

구문

    Sequence Point

        x = x++ * y--; 같이 한 구문에서 증/감 연산자를 사용하면 시스템마다 결과가 다름

        이는 Sequence Point 가 ; 이후이기 때문이고 그 이전은 순서가 보장받지 않기 때문.

        위의 증감 연산자는 Sequence Point 이후 Side Effect 인 증/감을 시행시키는 것.

    접두어/접미어

        접두어는 AAA ++() {}; 이런식이고 접미어는 AAA ++(AAA&) 이런식임 

        참고로 접미는 operator++(int) 이런식으로 뒤에 int 붙여아함.

        접미는 값의 복사본을 만들고 그 값을 증가시키고 그걸 리턴하는 방식으로 더 느림.

        참고로 *p++ 은 *p 를 하고 후에 p+1 이 일어남

    Comma Operator

        보통 for 문, 변수 선언에서 많이 응용되며 Comma 자체가 Sequence Point 임

        우선순위가 가장 낮음.

        int a = 1, int b = 2;  int a, b = 2;  a++, b++; 모두 가능.

        a=1, 100; 하면 a=1; 100; 과 같음

        int a = (1, 2); 하면 (1, 2) 가 수행된 결과인 2가 대입됨


함수

    리턴원리

        CPU 의 레지스터나 메모리에 return 타입 크기만큼의 공간을 둬서 값 복사.

    함수원형 쓰는 이유

        가끔 헷갈리는데 그냥 컴파일용임.

    자동타입변환 우선순위

        똑같음 >> 승급변환 >> 일반변환 >> 사용자정의 변환

        승급변환은 int->long, float->double 처럼 같은 타입 내의 변환

        일반변환은 int->float, long->double 처럼 다른 타입 간의 변환

        사용자 정의 변환은 생성자, 복사생성자 등을 이용한 변환

    

Template

    Compile

        헤더를 보고 필요한걸 만들어냄 = 특수화

        최종 컴파일된 파일엔 Template 은 없음.

    Specialization

        명시적 구체화

            특수화를 하지 않고도 함수의 정의를 만들라는 소리.

            template int Get<int>(int a);

            호출할 때 Get<int>(10) 하는 것도 명시적 구체화.

        명시적 특수화

            특정 타입만 구현을 다르게 하고 싶을 때 사용

            template<> int Get<int>(int a) {}  또는 

            template<> int Get(int a) {}  이런식.

            (구 버전은 int Get<int>(int a) {} 로 앞에 template 이 빠짐)

        우선순위

            매개변수가 정확한 함수 >> 명시적 특수화 >> 암시적 구체화

            암시적 구체화에서도 형변환이 가장 적게 일어나는걸 우선으로 둠

            ex) 아래의 경우 T = int* 로 해석해 형변환이 가장 적게 되는 위에게 선택

template<typename T>
void AAA(T a);

template<typename T>
void AAA(T* a);

int* a;
AAA(a);

    decltype

        template 에서 자동 형변환으로 인해 리턴 타입을 유추하기 어려울 경우 사용.

        decltype(수식) 이러면 적절한 타입을 유추해서 템플릿 타입 자리에 넣을 수 있음.

            이때 수식은 계산되지 않으며 그냥 return type 만 컴파일러가 검사함

        변수 타입에 & 를 넣으려면 수식 전체에 () 를 넣으면 됨 

            함수는 return type 을 reference 로 하면 가능

        auto

            auto AAA(T1 a, T2 b) -> decltype(a + b) {} 

            위와 같은 경우를 위해 사용

    수식 매개변수

        정수, 열거형, 참조, 포인터가 template<int N> 이런식으로 들어갈 수 있음.

        수식 매개변수는 상수이며 주소를 얻을 수 없음


Class

    자동생성

        Move Construct/= Oper 정의 시 Copy Construct 와 = Oper 는 자동으로 안나옴

    Overloading

        new, delete 도 되는거 알았음? 

            static method 로 가능하고 virtual 안됨.

        멤버변수로만 되는 연산자 "=, (), [], ->"

        명시적 타입 변환 연산자

            explicit Typename();

            method 만 되고 return type 안쓰지만 구현땐 return 함. 파라미터 x.

            explicit 안붙이면 실수 많이해서 붙임을 권장

        상속되지 않은 메소드 -> 생성자, 파괴자, =

    생성자

        Default 생성자를 쓸 땐 괄호쓰면 안됨. ex) AAA aaa() => x

        파라미터 있는 생성자는 됨. ex) AAA aaa(10); => o

    Virtual Table

        객체에 테이블 가리키는 포인터 추가. (64bit 이면 8byte 늘어남)

        가상함수 수만큼 테이블 크기는 늘어남.

        오버로딩을 쓰는 경우 모든 버전을 다시 적어야함(부모꺼 못씀)

        return value 만 point/ref 인 경우 부모에선 base, 자식에선 Derivate 가능

    Friend

        Header 에 함수를 쓸 경우 class 내부에만 정의을 하면 안되고 전방선언도 해야함.

            왜냐하면 메소드는 클래스 내부에서 외부함수는 찾지 않기 때문임.

        namespace 와도 관련이 없고 오히려 특정 역할을 수행하는 선언일 뿐임.

            class/method/function 이 자신의 접근자를 무시하게 한다는 역할일 뿐임.

            그래서 friend 키워드 앞에 private/public/protected 는 아무의미 없음

                friend function 의 경우 이걸 클래스 외부에서 call 하는거도 가능.

                이때 namespace 로 class name 은 붙이면 안됨

    Private/Protected 상속

        멤버변수로 두는거랑(Containment) 같은 일을 함.(Has-A).

        protected 와 private 의 차이는 파생의 파생이 기초에 접근가능한가임.

class P
{
private: // 의미 x
	inline friend void PrintFriend() {}
	
public:
	virtual ~P() = default;
	virtual void Get() { cout << "asdf" << endl;}
	int x1 = 10;
};


class C : private P
{
public:
	virtual ~C() = default;
	virtual void Get() {
		P::Get(); P::x1;
		P* p = this;
	}
	int x2 = 12;
};

class CC : protected C {
public:
	virtual void Get() {
		C::x2; //C::P::x1; 불가능
		C::Get();
		C* p = this;
		//P* p = this;     불가능
	}
};

int main()
{
	CC aaa;
	aaa.Get();
	//aaa.x2;      불가능
	//P& p = aaa;  불가능
	//C& c = aaa;  불가능
}

    using

        using 부모함수(); 하면 접근 제한자를 오버로딩 안하고도 재지정 가능함.

        typedef 와 같이 using aaa = 긴타입; 으로 간략한 사용 가능

    virtual 부모

        다중상속에서 하나의 공통부모만을 만들기 위해 공통부모 상속에 virtual 을 붙임.

            class AAA : virtual public (public virtual) P {}

        이때 파생 클래스의 생성자에서 명시적으로 공통부모의 생성자를 호출해야함.

            상속되는 여러 클래스들이 각각 공통부모의 생성자를 호출하면 말이 안되니까.

        그냥 순수 가상 함수만 있는 인터페이스만 다중상속을 쓰는게 편함.

    Class Template

        함수는 안되고 이거만 데이터형 매개변수에 기본값 지정 가능. 

            ex) template<typename T = int>

        객체가 요구되기 전까지 암시적 구체화는 일어나지 않음.

        friend 구체화

            나머진 그대로고 외부에 template 으로 만들어진 함수에 대해서가 복잡함.

template<typename T> void Print(T in) { cout << in.a << endl; }
template<typename T>
class AAA
{
public:
	friend void Print<>(AAA<T> in);
	friend void Print2(AAA<int> in); //같은 헤더에서 쓸거 아니면 여기다 정의해도 됨.
	T a;
};
void Print2(AAA<int> in) { cout << in.a << endl; }


int main()
{
	AAA<double> aaa{3.141592}; // template 경우 생성자가 자동으로 생성이 안되나봄
	Print(aaa);
	Print2(AAA<int>{5});
}

            

Error

    abort

        강제종료. catch 되지 않은 exception 에 대해서도 호출됨.

    Exception

        throw, try...catch, noexcept(애는 연산자도 됨)

        함수/메소드 정의 시 끝에 throw(std::bad_alloc) 등으로 에러 쓴다는거 알람가능

            그러면 사용자가 try...catch 를 써야한다는걸 알기 쉬움

            만약 타입이 다르면 std::bad_exception 호출

            근데 쓰지 말라함. 근데 noexcept 는 괜찮다 함.

        try...catch 혹은 main 가기 전까지 스택은 자동으로 풀림 => 간 후엔?

            main 은 terminate 함수가 abort 를 호출하나 set_terminate 로 커마가능

            main 은 set_unexpected 로 bad_exception 만 받을 수도 있음.

        exception 은 자동으로 복사본이 오기 때문에 catch 문은 참조를 받아야함.

        catch 후에 상속구조가 있는 예외가 온다면 가장 파생된거부터 배치해야함.


STL

    Iterator

        개념적으로 나눔

            입력/출력

                읽기 또는 쓰기 가능. ++ 연산자 가능하지만 바로 다음 원소가 아님.

                cout 등에 쓰임

            Forward => RW 가능. ++ 연산자 쓰면 바로 다음원소 나옴.

            Bidirectional => --연산자 가능

            Random Access => [n], iter + n, 등 포인터+정수 이게 됨.

        잘 보이는거

            reverse iterator => rend, rbegin 쓰면 나오는거.

            insert iterator

                back, front, banilla 가 있는데 queue 같은게 front 못씀.

                front/back 을 지원하면 앞뒤에 고정시간 삽입이 가능함을 알수있음. 

front_insert_iterator<vector<int>> iter1(v);  // 지원안함
back_insert_iterator<vector<int>> iter2(v);
copy(v.begin(), v.begin() + 10, iter2);



댓글 없음:

댓글 쓰기

List