[TOC]
구조체와 클래스의 차이
- C에서의 구조체는 변수를 묶어주는 역할을 하고 메서드를 포함 할 수 없다.
- C++에서 구조체는 클래스와 거의 동일한 정의/선언 형식과 사용방법을 가진다.
- 차이점은 범위접근 지정자를 명시하지 않았을 시 기본 범위접근 지정자가 다르다.
- 구조체는 기본으로 public 이고 클래스는 기본으로 private 이다.
- 구조체도 클래스처럼 함수 선언 및 정의, 생성자 및 소멸자 선언 및 정의, 상속 구조를 사용할 수 있다.
- 차이점은 범위접근 지정자를 명시하지 않았을 시 기본 범위접근 지정자가 다르다.
C와 C++에서의 구조체
- C에서의 구조체는 데이터를 묶어주는 용도일뿐이라서 함수선언을 할 수 없고 생성자, 소멸자 개념이 없다.
- C++에서의 구조체는 사실상 클래스와 같은 것으로 기본 범위지정이 다를뿐이다.
cast 정리
윈도우 프로시저에 클래스 멤버 함수를 전달하기
- 윈도우 프로시저는 함수를 받는데 이곳에 클래스 멤버 함수를 전달할 수 없다.
- 만약 전달할 수 있다해도 this 객체를 지정할 수 없기 때문에 호출 할 수 없다.
STL
vector의 원소를 참조할 때 복사가 발생하나? 복사가 발생하지 않게하려면 어떻게 하나?
변수 선언하며 받는것과 이미 선언된 변수에 받는 것에 && 복사생성자 호출에 차이가 있나?
배열접근 연산자로 접근하면 복사가 발생하나? 포인터로 받으면 복사가 발생하나?
라이브러리
정적 라이브러리(Static Library)
- 미리 만들어진 정적 라이브러리 파일(lib)에 있는 함수 및 데이터를 헤더 파일을 이용해 사용하고 링크 작업 시 함수와 데이터가 실행파일에 포함(복사)된다.
- Visual Studio에서 정적 라이브러리를 포함하는 프로젝트를 빌드하면 정적 라이브러리 프로젝트도 자동 빌드되어 .lib 파일이 만들어진다.
- 이후 정적 라이브러리의 변경이 없으면 이미 만들어진 .lib 파일을 사용하고 변경됐다면 함께 빌드된다.
- Win32 Static Library 프로젝트로 만들어진 라이브러리의 함수 및 데이터는 #include 전처리 명령어를 통해 사용할 수 있다.
- 장점
- 실행 파일에 모든 라이브러리들이 존재하고 그것이 정확한 버전인 것을 확신할 수 있기 때문에 동적 라이브러리(DLL)의 의존성 문제를 피할 수 있다.
- 어플리케이션이 단일 실행파일에 모두 포함되므로 배로와 설치가 간단하다.
- 참조하는 라이브러리를 부분적으로 포함하기 때문에 전체 라이브러리를 로드하는 동적 라이브러리는 보다 파일 크기가 줄어들 수 있다.
- 단점
- 라이브러리에 버그가 있을 경우 수정하여 실행파일 전체를 다시 배포해야한다.
- 윈도우는 lib, 리눅스는 .a 확장자를 사용한다.
정적 라이브러리 생성 및 사용
- 생성
- 방법1.
- 새 프로젝트 추가 > 정적 라이브러리
- 프로젝트 속성 > 일반 > 구성 형식 > “정적 라이브러리(.lib)” 라고 표시됨
- 방법2.
- 프로젝트 속성 > 일반 > 구성 형식 > “응용 프로그램(.exe)”을 “정적 라이브러리(.lib)” 로 변경
- 방법1.
- 사용
- 정적 라이브러리를 사용할 프로젝트에서 “우클릭 > 추가 > 참조” 선택하고 사용할 정정 라이브러리 프로젝트를 선택
- 정적 라이브러리 프로젝트는 설정에 링커 항목이 없다.
공유 라이브러리(Shared Library)
동적 링크(Dynamic Link)
- 라이브러리의 코드와 어플리케이션의 코드가 프로그램 실행 시 메모리에 로드되는 시점에 링크된다.
- 실행 파일에는 함수의 호출 방법만 있고 어플리케이션이 실행되고 라이브러리가 로드된 시점에 함수의 주소를 알 수 있다.
- 여러 어플리케이션에서 하나의 라이브러리를 공유하기 때문에 메모리를 효율적으로 이용한다.
- 윈도우는 .dll, 리눅스는 .so 확장자를 사용한다.
- 어플리케이션 실행 시 라이브러리 코드를 모두 메모리에 읽어놓고 실행된다.
동적 로드(Dynamic Load)
- 라이브러리 로드 시점이 어플리케이션 실행 시점이 아니라 어플리케이션 내부에서 필요 시점에 로드한다.
- 프로그램 동작 상황에 따라 필요한 라이브러리와 함수를 로드할 수 있다.
- 윈도우는 .dll, 리눅스는 .so 확장자를 사용한다.
VS에서 프로젝트끼리 참조하기
- 다른 프로젝트의 소스파일 경로를 추가 포함 디렉토리로 설정하면 그 프로젝트의 코드를 사용할 수 있다.
- 이때 .lib 파일 또는 프로젝트를 참조에 추가하지 않으면 링크 시 에러가 난다.
- 프로젝트의 결과가 .exe라면 링크 시 .lib 파일을 찾을 수 없기 때문에 에러가난다.
- 따라서 프로젝트끼리의 참조는 참조 당하는 프로젝트가 .lib 파일을 출력해야 정상적으로 컴파일된다.
VS 프로젝트 설정에서 참조란?
소스 코드는 포함 디렉토리, 추가 포함 디렉토리에 경로를 추가하여 참조함 같은 프로젝트에 있는 소스코드가 아니므로 이를 컴파일한 lib 파일에서 정의부분을 링크시킴 같은 솔루션 내에 있는 프로젝트라면 이 lib 파일이 있는 경로를 따로 넣어주지 않고 참조에 프로젝트를 추가하면 됨 다른 솔루션에서 컴파일한 lib 파일만 제공된다면 이 lib 파일이 있는 경로를 “링커 > 입력 > 추가 종속성”에 추가해야함. 같은 솔루션 내에 있는 프로젝트라도 “일반 > 구성 형식”이 응용 프로그램이라면 lib 파일이 생성되지 않기 때문에 링크 시 에러가 남(LNK2019 확인할 수 없는 외부 참조입니다) 따라서 참조되는 프로젝트의 “일반 > 구성 형식”의 값을 “정적 라이브러리”로 변경해서 .lib 파일이 생성되도록 해야함. 하지만 이렇게 하면 실행파일이 없기 때문에 실행되지 않음. 따라서 실행이 되는지 정도의 테스트를 위해 사용해야함.
가상함수 호출 순서
- 값 변수
- 생성자
- 부모 -> 자식 순으로 호출
- 소멸자
- 자식 -> 부모 순으로 호출
- 일반 함수
- 변수 타입의 함수가 호출(부모의 함수는 호출되지 않는다)
- 가상 함수
- 변수 타입의 함수만 호출(부모의 함수는 호출되지 않는다)
- 생성자
- 포인터 변수
- 생성자
- 변수타입과 상관없이 실제 객체의 생성자가 부모 -> 자식 순으로 호출
- 소멸자
- 가상 소멸자가 아닌 경우
- 변수 타입의 소멸자가 호출되며 부모 클래스가 있는 경우 자식 -> 부모 순으로 호출
- 가상 소멸자인 경우
- 실제 객체의 소멸자가 호출되며 부모 클래스가 있는 경우 자식 -> 부모 순으로 호출
- 가상 소멸자가 아닌 경우
- 일반 함수
- 변수 타입의 함수만 호출(부모의 함수는 호출되지 않는다)
- 가상 함수
- 실제 객체의 함수만 호출(부모의 함수는 호출되지 않는다)
- 생성자
상속관계가 있을 때 소멸자를 가상함수로 지정해야하는 이유
- 소멸자를 가상함수로 지정하지 않으면 소멸 시 변수 타입으로 지정된 클래스의 소멸자만 호출된다.
- 유도 클래스의 변수가 소멸될 때 부모 클래스의 소멸자는 호출되지 않는다.
- 소멸자를 호출하지 않는다면 메모리릭의 가능성이 있기 때문에 가상함수로 설정하여 부모 클래스의 소멸자가 호출되도록 해야한다.
- 가상함수는 해당 클래스의 가상함수가 호출된 다음 상위 클래스의 가상함수가 순차적으로 실행된다.
다형성
- 상속관계에 있는 클래스의 객체는 상위 클래스 타입의 변수에 객체의 포인터를 넣을 수 있다.
- private 상속받았다면 상위 클래스 타입의 변수에 넣을 수 없다.
인터페이스
- 소멸자와 순수 가상함수만 선언된 클래스
- Visual C++의 interface
- 특별한 것이 아닌 일반 struct형을 재정의한 자료형에 불과하다.
- 인터페이스로 선언되었음을 명시적으로 보이게 하기 위함이 전부이다.
- 시각적으로 다르게 보여준다.
vector에는 배열을 저장할 수 없다.
- 표준 라이브러리 컨테이너는 복사 및 할당이 가능한 것만 저장이 가능하다.
- 배열은 복사 및 할당이 불가능하므로 컨테이너에 저장할 수 없다.
- 기본 자료형처럼 복사가 가능하거나 구조체처럼 복사 생성자 등이 있어야한다는 말인듯하다.
- 굳이 배열을 vector에 저장하려면 구조체를 만들어야 한다.
C++에서의 static
- 전역 변수에서 사용
- ‘내부 연결 속성’이 적용되어 소스파일 내에서 사용가능하다
- 지역 변수에서 사용
- 자동 주기에서 정적 주기로 바뀌게 되어 스포프가 종료된 후에서 값을 유지한다.
구글의 C++ 코드 컨벤션
- 멤버 변수는 항상 private으로 선언하고 접근자 함수를 제공하라.
- 인라인 함수는 10라인이하의 함수만 정의하라.
- 생성자에서 오류가 날 수 있거나 복잡한 코드를 피하라.
- 생성자에서 모든 멤버변수를 초기화해라.
- 하나의 인자만 받는 생성자는 explicit으로 선언하여 암묵적으로 생성자가 호출되지 않도록 하라.
- Foo(string name) 생성자가 정의된 Foo를 인자로 받는 함수에 string을 전달하면 Foo객체를 생성하며 Foo(string name) 생성자를 호출할 수 있다. 이렇게 string을 의도와 다르게 전달할 수 있는 가능성을 없애야한다.
- 복사생성자와 대입연산자가 암시적으로 호출되지 않게 숨겨라.
- 복사가 가능해야하는 클래스는 거의 없다.
- 복사 대신 포인터나 레퍼런스를 사용할 수 있다.
- 복사가 가능해야한다면 CopyFrom(), Clone() 등 메서드를 만들어라.
- 만약 복사 생성자가 필요하다면 대입연산자와 함께 작성하여 관리하라.
- 복사 생성자와 대입연산자를 private 선언하여 사용할 수 없게 만들어라.
- 구조체는 데이터를 나르는 수동적인 객체의 경우에만 사용하고 그 외는 클래스를 사용하라.
- 구조체가 메서드를 가질 수는 있으나 멤버 변수의 값일 읽고 쓰는 이외의 기능을 가져서는 안된다.
- 대부분 구현상속(is a)보다 컴포지션(has a - 합성)이 더 적절하다.
- 상속은 코드가 분리되어 이해하기 어렵고
- 상위 클래스가 물리적 레이아웃을 정해버리고
- 하위 클래스가 구현을 변경할 수 없다.
- 다중상속이 유용한경우는 거의 없다. 하지마라.
- 클래스의 연산자 오버로드를 하지마라.
- 클래스를 마치 내장 타입인 것처럼 연산자를 재정의하면 내장 연산자인듯 착각하게 하여 의도치 않은 작동을 찾기 힘들어진다. 또한 비싼 연산을 값이 싼 내장 연산으로 착각하게 한다.
- 포인터에서도 작동하는 연산자는 버그를 만들기 쉽다.
- 가능한 함수를 짧게하고 하나의 기능만 하다록 작성하라.
- 동적으로 할당된 객체는 하나의 고유한 소유자를 두는 것을 선호하라. 스마트 포인터를 써서 소유권을 이전하는 것을 선호하라.
- 할당한 코드에서 소유권을 유지하도록 하고 다른 코드에 전달해야할 경우 소유권 이전보다 복사본또는 포인터, 레퍼런스를 전달하라.
- 소유권 이전을 명시적으로 하기 위해 unique_ptr 사용을 선호하라.
- 레퍼런스로 전달되는 모든 인자는 const로 수직되어야한다.
- 함수가 인자를 변경할 필요가 있는 경우 인자는 포인터를 사용해야한다.
- 레퍼런스로 선언하므로 함수내에서 (*p) 처럼 사용하지 않아도 되고, null 값을 가질 수 없는 것이 명확해진다.
- 구글 코드 컨벤션에는 입력 인자는 값이거나 const 레퍼런스이고 출력 인자는 포인터이다.
- 레퍼런스 : 사용하겠다.(사용만 하기 때문에 const로 정의해야한다.)
- 포인터 : 조작하겠다.
- rvalue 레퍼런스를 사용하지 말라.
- 이건 잘 모르겠다.
- rvalue가 복사 생성 시 중간에 쓸데없이 객체를 생성하지 않고 바로 원본을 옮기기 위함인데 사용하지 말라는 이유가 이해안된다.
- 좀 더 내공을 쌓고 생각해보자.
- 이건 잘 모르겠다.
- 함수 오버로드는 가급적 사용하지 않는다.
- 함수가 인자의 타입으로만 오버로드 된 경우 호출 이유를 파악하려면 C++의 복잡한 매치 규칙을 이해해야한다.
- 디폴트 인자 사용을 고려해라.
- 예외를 사용하는 코드를 만들지마라.
- RTTI(Run-Time Type Infomation) 사용을 피한다.
- 실시간에 객체의 타입을 질의한다는 것은 설계에 문제가 있다는 뜻이다.
- 유닛테스트에서는 자유롭게 사용할 수 있다.
- static_cast<>()와 같은 C++ 형 변환을 사용하라.
- 스트림을 사용하지 말고 printf(), scanf()를 사용하라.
- 이유가 잘 이해되지 않는다.
- 증감 연산자(++, –)의 리턴값이 무시될 경우 증감 연산자는 앞에 사용하라.
- 후위 연산자는 복사본을 생성하므로 리턴값이 무시되는 상황에서는 전위 연산자가 무조건 유리하다.
- 일리가 있다고 생각되면 항상 const를 사용하라.
- 코드를 읽기 쉽게 만들고 컴파일러가 좀 더 좋은 코드를 생성할 수 있다.
- C++11 에서 진짜 상수를 정의하거나 상수 초기화를 확실히 하기 위해 constexpr를 사용하라.
- 정수
- 크기를 보장해야할 필요가 있을 때 short, unsigned long long보다
에서 제공하는 int16_t, uint32_t, int64_t와 같은 타입을 사용하라. - 분명한 목적(비트 패턴을 표현, 오버플러우의 나머지 연상을 정의)이 없다면 부호없는 정수를 사용하지 말라. 특히 음수가 되지 않는 것을 말하기 위해 부호없는 타입을 사용하지 말라. 대신 assertion을 사용해서 이를 보장하라
- 크기를 보장해야할 필요가 있을 때 short, unsigned long long보다
- 매크로를 사용하지 말라.
- 매크로는 포르그래머가 보는 코드와 컴파일러가 보는 코드를 다르게 만든다. 또한 매크로는 전역범위를 가지므로 예상치 못한 결과를 초래한다.
- 매크로를 사용하지 않고 같은 결과를 얻을 수 있는 다른 방법을 고려하라.
- 매크로 대신 인라인 함수 사용.
- 상수 매크로 대신 const 변수 사용.
- 긴 변수이름 축약에는 레퍼런스 사용
- 조건부 코드 컴파일은 아예 하지 말라.
- null을 나타내기 위해 다음을 사용하라.
- 정수는 0
- 실수는 0.0
- 포인터는 nullptr
- char는 ‘\0’
- sizeof(type)보다 sizeof(varname)을 사용하라.
- 변수의 타입이 변경된 경우도 sizeof(varname)은 정상 작동한다.
- 지역변수에만 auto를 사용하라.
- 람다 표현식, std::function, std::bind는 사용하지 말라.
- 람다의 변수 캡쳐는 사용하기 까다롭고, dangling-pointer 버그의 온상이 될 수 있다.
- 왜 람다가 dangling-pointer 버그의 온상이지?
- 길게 중첩된 익명함수는 이해하기 어렵다.
- 람다의 변수 캡쳐는 사용하기 까다롭고, dangling-pointer 버그의 온상이 될 수 있다.
- Boost 라이브러리에서 공인된 라이브러리만 사용
- 이름 규칙
- 함수, 변수, 파일이름
- 약어를 피하고 서술적으로 짓는다.
- 파일이름
- 모두 소문자이고 _, -를 포함할 수 있다.
- 타입이름
- 카멀케이스를 사용, 언더스코어를 사용하지 않는다.
- 변수 이름
- 모두 소문자로 작성
- 단어 사이에 언더스코어를 사용
- 멤버 변수 이름
- 변수 이름과 동일
- 이름 끝에 언더스코어를 붙임
- 이게 좋은건지 잘 모르겠다.
- 구조체 변수 이름
- 변수 이름과 동일
- 이름 끝에 언더스코어를 붙이지 않음
- 전역변수
- g_와 같이 로컬 변수와 구분되는 접두어를 사용
- 상수 이름
- k로 시작하고 캐멀케이스를 사용한다.
- 함수 이름
- 일반 함수는 캐멀케이스를 사용
- 변수의 접근자와 변경자는 해당 변수의 이름과 같이 사용
- 예) num_count
- 접근자 : num_count()
- 변경자 : set_num_count(..)
- 예) num_count
- 함수 실행중 크래쉬 발생 가능성이 있다면 이름 끝에 OrDie를 붙인다.
- 이주 짧은 인라인 함수는 소문자를 사용할 수 있다.
- 네임스페이스 이름
- 모두 소문자로 작성
- 프로젝트 이름과 디렉터리 구조에 기반하여 작성
- 열거형 이름
- 상수 또는 매크로 방식중 하나로 작성
- kEnumName or ENUM_NAME
- 매크로 이름
- 매크로 사용하지 마라.
- 사용하려면 대문자 중간에 언더스코어를 넣어라.
- 함수, 변수, 파일이름
- 주석
- // 이 /**/보다 더 많이 사용된다.
- 클래스
- 이 클래스가 무엇이고 어떻게 사용하는지 설명해야한다.
- 포매팅
- 줄 길이
- 80문자를 넘지 않게 한다.
- 탭 대신 스페이스 2개를 사용하라.
- 전처리기 지시자는 줄 처음에 있어야함
- 코드와 같이 들어쓰기가 되어선 안됨
- 네임스페이스 내부는 들여쓰기하지 않는다.
- 빈줄 사용을 최소화하라.
- 함수 사이의 빈줄은 가독성에 도움을 주지 않는다.
- 최대한 더 많은 코드가 한 화면에 들어올 수 있게 하라.
- 하지만 너무 조밀한 것도 가독성을 해치므로 각자의 판단에 맡긴다.
- 줄 길이
MS의 C++ 코드 컨벤션
https://docs.microsoft.com/ko-kr/windows/win32/stg/coding-style-conventions
- 대부분 이름과 축약어 사용에 대한 내용이다.
- 구글 코드 컨벤션과 적절히 조합하여 사용해야겠다.
- 윈도우 프로그래밍을 할 경우에는 윈도우 API와의 일관성을 위해서 MS의 스타일을 지키는 것이 좋은데 변수명에 작성이 많이 다르다.
- 캐멀 vs 스테이크
- 둘다 장단점이 있어서…
- 캐멀
- 변수 타입을 알 수 있다.
- 모든 클래스마다 변수타입을 축약어로 만드는게 힘들다.
- 타입 이름을 첫글자를 소문자로만 바꿔도 변수명으로 사용할 수 있다.
- 변수의 타입이 변경될 경우 이름까지 바꿔야한다.
- 스네이크
- 가독성이 좋다.
- 변수명이 너무 길어질 수 있다.
- 변수 타입을 알 수 없다.
- 타입병과 번수명이 확연히 다르므로 변수명을 복붙할 수 없다.
- 사실 이게 가장 불편할 듯하다.
- 그냥 윈도우 스타일로 해야겠다.
- public 멤버변수에도 타입 축약어를 맨 앞에 붙인다.
- private 멤버는 public 멤버와 같고 _를 맨 앞에 붙인다.
- 들여쓰기도 탭으로 유지
- 클래스 이름 앞에 C를 붙이면 변수명에 소문자 c를 붙이면 되겠다.
포프의 C++ 코딩 스탠다드
https://docs.google.com/document/d/1cT8EPgMXe0eopeHvwuFmbHG4TJr5kUmcovkr5irQZmo/edit#
클래스 선언 시 자기자신의 클래스 타입인 멤버변수를 선언하기
- 이런 재귀적 포함관계는 정확한 메모리 할당 크기를 알 수 없기 때문에 불가능하다.
- 인스턴스 안에 같은 크기의 인스턴스를 할당하고 그 안에 또 같은 크기의 인스턴스를 할당해야하니 컴파일러가 거부한다.
- 포인터는 고정된 크기를 가지므로 포인터로 선언하면 가능하다.
- https://www.gpgstudy.com/forum/viewtopic.php?t=11450
VS 콘솔 프로젝트에서 Direct3D 사용
- DirectX9를 설치한 경우
- SDK의 헤더와 lib 파일의 경로를 잡아준다.
- DirectX9를 설치하지 않고 윈도우에 포함된 SDK 사용할 경우
- #include
- #include
- 프로젝트 “속성 > C/C++ > 전처리기 > 전처리기 정의”에 ‘_XM_NO_INTRINSICS_’ 추가
- #include
상속생성자
- 정리 필요.
class B {
int v_;
public:
B() : B(0) {} // C++11의 위임생성자 기능 활용
B(int v) : v_(v) {}
int get();
void set(int v);
};
class D : public B {
float v2_; // 초기화 필요
public:
using B::B; // 상속 생성자
void compute();
};
출처: https://yesarang.tistory.com/374 [김윤수의 이상계를 꿈꾸며]
위임 생성자
- 정리 필요.
에러
2019 링크 에러 시 확인 사항
- 상속관계가 있을 때 하위 클래스의 cpp 파일에 상위 클래스의 cpp 파일을 #include 해야한다.
- lib 파일 추가돼야한다.
- 프로젝트에 헤더(.h), 구현(.cpp) 파일이 추가돼야한다.
- 빌드 모드(debug, release) 확인
- .c, .cpp 파일이 같은 프로젝트에 존재하면 안된다.
- inline 함수의 선언과 정의를 다른 파일로 분리하면 안된다.
- template 사용 시 선언과 정의를 다른 파일로 분리하면 안된다.
- 함수만 선언하고 구현을 하지 않으면 안된다.
- TransparentBlt을 사용할 경우 msimg32.lib;를 “속성 > 링커 > 입력 > 추가 종속성”에 반드시 추가해야한다.
- 프로젝트 참조 시 참조되는 프로젝트는 .lib 파일을 생성하는 동적/정적 라이드러리 프로젝트여야한다. 그렇지 않고 일반 프로젝트라면 구현부분을 링크걸 .lib 파일이 없기 때문에 소스코드에 cpp 파일을 포함(#include) 시켜야한다.
- 확실하지 않다. 확인요.
- https://docs.microsoft.com/ko-kr/cpp/error-messages/tool-errors/linker-tools-error-lnk2019?f1url=https%3A%2F%2Fmsdn.microsoft.com%2Fquery%2Fdev15.query%3FappId%3DDev15IDEF1%26l%3DKO-KR%26k%3Dk(LNK2019)%26rd%3Dtrue&view=vs-2019
C2512 : 사용할 수 있는 적절한 기본 생성자가 없습니다.
- 생성자를 명시적으로 정의하지 않은 경우 컴파일러가 생성자를 정의해준다.
- 사용자 정의 생성자를 정의하면 컴파일러는 생성자 정의에 관여하지 않으며 기본 생성자도 만들어주지 않는다.
- 이렇게 사용자 정의 생성자를 만들었을 때 기본 생성자를 사용하는 코드가 있으면 “기본 생성자가 없다”는 에러가 나는 것이다.
- 기본 클래스에서 기본 생성자를 정의하지 않고 유도(자식) 클래스에서 기본 생성자를 사용할 경우 생성자 호출 순서에 의해 기본 클래스의 기본생성자 -> 자식 클래스의 기본생성자를 호출하는데 이때 기본 클래스의 기본생성자가 없으므로 에러가 난다.
- 해결 1.
- 기본 클래스에 기본 생성자를 정의한다.
- 해결 2.
- 하위 클래스의 생성자에 초기화 코드를 넣어 기본 클래스의 사용자정의 생성자를 호출하도록한다.
- 이는 기본 클래스의 기본 생성자를 사용하는 코드를 없애므로써 명시적으로 생성자를 호출하므로 기본 생성자를 찾아 호출하지 않게 된다.
- 해결 1.
부동소수점 타입의 변수는 static 클래스의 static 멤버라도 클래스 내부에서 초기화 할 수 없다.
static class A{
static int i = 1; // 가능
static float f = 1.0 // 불가능, 에러: "const float" 형식의 멤버에는 in-class initializer를 사용할 수 없습니다.
}
- 표준에서 허용하지 않기 때문
- 가능한 컴파일러는 표준을 확장한 것이다.
- VS에서는 불가능하다.
std::vector::erase() 사용 시 “삭제된 함수를 참조하려고 합니다” 컴파일 에러.
- 이동 할당 연산자(T& operator=(T&&)) 또는 복사 할당 연산자(T& operator=(const T&))가 없기 때문
- 할당 생성자가 필요한 이유
- erase()는 해당 인덱스의 객체를 제거하고 뒤쪽의 객체를 당겨겨오기 위해 복사 또는 이동 시키기 때문
- 이동 또는 복사 할당 연산자가 없는 이유
- 사용자 정의 생성자를 만들 경우 컴파일러가 복사 할당 연산자를 생성하지 않기 때문.
memcpy 로 구조체, 클래스 복사 문제
- memcpy는 단순하게 원본 포인터에서 주어진 바이트 수만큼 복사를 한다.
- 포인터 멤버변수의 주소에가서 값을 복사해주지 않는다.
- 따라서 클래스의 모든 멤버변수가 기본형이라서 데이터가 연속된 메모리 공간에 있지 않다면 사용해선 안된다.
- 에러 케이스
- deque처럼 가변적으로 길이가 변할 수 있는 멤버변수가 있는 클래스의 객체를 vector에 저장
- vector의 원소를 지우기 위해 vector.erase()를 호출
- erase()가 호출되어 중간 원소가 제거되면 뒤의 원소를 당겨 채우기 위해 복사 대입 연산자 호출
- 복사 대입연산자에서 memcpy()로 메모리 복사
- 에러 발생
- 해결
- 복사 대입 연산자에서 멤버 변수를 모두 제대로 복사
- vector에 포인터 형식으로 저장
fatal error C1010: 미리 컴파일된 헤더를 찾는 동안 예기치 않은 파일의 끝이 나타났습니다.
- VS에서 자주 쓰는 헤더 파일들을 미리 컴파일 해서 전체 컴파일을 빠르게 하기 위한 설정 때문이다.
- 간단하게 해결하려면 “속성>C/C++>미리컴파일된 헤더”를 사용안하면된다.
- 미리 컴파일된 헤더를 사용하면서 오류를 해결하려면 오류가난 .cpp 파일일에서 가장 먼저 #include “stdafx.h”를 선언해주면 된다.
LNK2001 외부 기호를 확인할 수 없습니다
- static 변수는 선언 후 클래스 외부에서 정의를 해줘야하는데 정의가 되어있지 않아서 나는 에러.
- 다음과 같이 정의
- Type Class::MemberName; or Type Class::MemberName = 초기화;
상호참조로 인한 에러(구문 오류 ‘;’…)
- 헤더파일 끼리 서로 include 해서 일어나는 문제
- 컴파일러가 헤더파일을 먼저 컴파일 하는 과정에서 상호참조 무한 루프를 돌기 때문
- 컴파일시에는 선언이 필요하고 컴파일 후 정의(구현)된 코드와 연결(링크)시키기 때문
- 해결
- A, B 클래스가 서로 상호참조하고 있다고 가정
- 헤더(.h)와 구현(.cpp)를 분리
- A.h에서 B.h 헤더를 include한 경우
- B.h 헤더에서는 A.h를 include하면 안되고 class A 이렇게 선언만 함
- A.cpp, B.cpp에서는 자유롭게 A.h, B.h를 include해도 됨
vector의 요소를 포인터로 저장하고 있을 경우 Dangling Pointer가 되서 에러나는 경우
- vector에 요소를 추가할 때 미리 잡아 놓은 공간이 다 찼을 경우 더 큰 메모리를 확보하여 요소를 이동시킨다.
- 이 때 기존에 참조하여 저장해놓은 포인터는 갱신되지 않기 때문에 포인터로 요소에 접근 시 엉뚱한 값을 참조하게 되고 프로그램이 종료될 수 있다.
- list 처럼 연속 메모리 공간을 사용하는 컨테이너를 사용하지 않아야한다.
- 확인 필요
- deque은 필요한 메모리가 있으면 추가 메모리를 확보하고 논리적으로 앞 뒤로 붙인다고 한거 같은데..
- 확인 필요
C2061 구문 오류: 식별자 ‘DXGI_JPEG_AC_HUFFMAN_TABLE’
- Windows10에서 DirectX9을 사용할 때 나는 에러
- 일반적으로 Direct3D 프로젝트를 생성했을 때는 나지 않고 MFC를 사용하는 프로젝트에서 Direct3D를 사용할 때 이런 에러가 난다.
- 원인
- Windows10에 기본으로 포함된 DirectX의 버전이 설치한 DirectX9보다 높은데 컴파일 시 설치한 DirectX9를 먼저 참조하게 프로젝트 설정이 돼있기 때문
- 해결
- 프로젝트 설정에서 “포함 디렉토리”에 설치한 DirectX9의 경로를 추가해준다.
- 보통 “추가 포함 디렉토리”에 추가하지만 이렇게 하면 이 디렉토리를 먼저 참조하는 듯.
- 삽질
- Windows SDK 폴더에 있는 rpcsal.h 파일로 설치한 DirectX9에 있는 rpcsal.h 파일을 덮어 쓰라는 말도 있지만 이 방법으로는 해결되지 않음.다른 경우라면 해결될지도 모르겠음.
- C:\Program Files (x86)\Windows Kits\10\Include\10.0.16299.0\shared\rpcsal.h
- 포함 디렉토리에 DirectX9 경로 추가로 해결이 된 상태에서 rpcsal.h 파일을 Windows Kits에 있는 것으로 덮어 써도 잘 됨. 이게 잘 되는것도 이상함.
- Windows SDK 폴더에 있는 rpcsal.h 파일로 설치한 DirectX9에 있는 rpcsal.h 파일을 덮어 쓰라는 말도 있지만 이 방법으로는 해결되지 않음.다른 경우라면 해결될지도 모르겠음.
TransparentBlt을 사용시 컴파일 에러
- TransparentBlt을 사용할 경우 msimg32.lib;를 “속성 > 링커 > 입력 > 추가 종속성”에 반드시 추가해야한다.
.inl 파일
- inline 함수는 선언과 정의를 동시에 하거나 하나의 파일에서 선언과 정의를 해야한다.
- inline 함수 파일이 커질 경우 선언과 정의를 분리하기 위해 헤더파일에 선언하고 헤더파일 끝에 #include로 정의 파일을 포함시킨다.
- 이때는 inline 키워드를 반드시 써줘야한다.
- .inl 파일 속성의 항목형식을 ‘C/C++ 컴파일러’에서 ‘C/C++ 헤더’로 변경해줘야 컴파일된다.
##
참고
-
https://sosal.kr/668?category=234063
-
https://ko.wikipedia.org/wiki/%EC%A0%95%EC%A0%81_%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC
-
https://m.blog.naver.com/kr_dukie27/10175747579
-
https://m.blog.naver.com/PostView.nhn?blogId=nawoo&logNo=220409027910&proxyReferer=https%3A%2F%2Fwww.google.com%2F