항목 1: C++를 언어들의 연합체로 바라보는 안목은 필수
C++은 Multiparadigm Programming Language다.
- C
- Object Oriented C++
- Template C++
- STL
C++?
절차지향 C → 객체지향 기능(클래스, 캡슐화, 추상화, 상속, 다형성)을 추가 → 템플릿(일반화), STL 추가
==> 하나로 정해진 언어가 아니라서 선택한 언어 규칙에 맞춰야함
(C++: call by reference / STL은 C 포인터를 본따 만들었기 때문에 call by value 사용하기)
(템플릿 관련 참고: https://www.youtube.com/watch?v=a6BQphLoTag)
항목 2: #define을 쓰려거든 const, enum, inline을 떠올리자
선행처리자(#define)보다 컴파일러(const, enum, inline)를 더 가까이 하자.
#define은 symbol table에 올라가지 않아 추적이 불가, 디버깅이 힘들다.
(symbol table: 변수의 의미를 추적하기 위해 컴파일러에서 생성 및 유지 관리하는 중요한 데이터 구조)
(즉, 범위에 대한 정보, 변수 및 함수 이름, 클래스, 객체와 같은 다양한 엔티티의 인스턴스에 대한 정보를 저장)
(enum과 달리 숫자 상수로 되어있어 어디에서 컴파일 에러가 발생했는지 알기 힘들다.)
const, enum은 여러 번 사용하더라도 한 개의 사본만 생긴다.
선행처리자, 매크로는 여러 개의 사본이 생성된다.
(선행처리자는 컴파일 단계에서 #define이 사용되는 곳에 동일한 코드들이 복사된다.)
(때문에 바이너리 파일 사이즈도 증가함)
- 상수 타입: const 붙이기
- 클래스 상수는 #define으로 정의하면 유효범위가 모호해짐(컴파일 끝날 때까지), 캡슐화도 안됨
- static const double Factor = 1.5;
클래스 상수는 static으로 선언, 선언부에서 초기화 하기 - 멤버 배열 선언 사이즈를 변수로 받게 하는 방법 --> enum hack
- static const double Factor = 1.5;
- 매크로 함수 대신 inline 템플릿 사용
- 장점: 클래서 유효범위, 접근 규칙 적용
항목 3: 낌새만 보이면 const를 들이대 보자!
함수 뒤에 const를 붙이는 경우?
- 어떤 함수가 객체를 변경시키고, 시키지 않는지 한 눈에 알 수 있게 해줌
- 상수 객체를 사용할 수 있게 함.
int value = 0;
int temp = 12;
///////////////////////////////////
//포인터가 가리키는 공간이 const
const int* ptr = &value;
*ptr = 10; //ERROR; 주소값은 const로 변경 불가
value = 1;
ptr = &temp; //가리키고 있는 값은 바꿀 수 없지만 주소솟값은 바꿀 수 있음 (const int(O)임 const int*(X))
///////////////////////////////////
//상수 포인터
int* const const_ptr = &value;
*const_ptr = 10; //공간은 const가 아니기에 가리키는 값 변경 가능
const_ptr = &temp; //ERROR; 주소 변경 불가
///////////////////////////////////
//상수를 가리키는 상수 포인터
const int* const cptr = &value;
*cptr = 10; //ERROR
cptr = &temp; //ERROR
///////////////////////////////////
//함수에 사용하는 cosnt
class CClass
{
private:
int m_num;
int* p_ptr;
public:
int foo(const int num){} //함수 내에서 파라미터 수정 방지
void print() const //const 멤버함수; const 객체는 const멤버 함수만 호출 가능
{
m_num = 0; //ERROR 함수 내에서 멤버 변수 변경 불가
}
const int& get() //반환값이 const, 상수 레퍼런스; 반환된 값을 변경하지 못하도록
{
return p_ptr; //상수 레퍼 변수만 받을 수 있음
}
}
포인터 관련 예시 이미지
STL iterator
--> 포인터가 가리키는 대상 변경을 막기 위해서 const_iterator 활용하기
* iterator 앞에 const를 붙이는 것과 const_iterator를 붙이는 차이를 정확하게 알아야함
비트 수준 상수성?
컴파일러가 지켜주는, 어떤 멤버 함수가 그 객체의 어떤 데이터 멤버도 변경하지 않는다. (대입연산)
--> const 객체여도 return char&인 경우 밖에서 수정될 수 있다.
--> mutable을 선언하여 값을 변경하는 것은 지양해야함.
const 유무로 오버로딩도 됨
ㄴ const 멤버함수: 상수 객체에 대해 호출될 함수를 뜻함
--> 상수/비상수 버전 따로 제공하면서 중복 코드와 유지보수가 힘들어짐
--> const_cast 사용하자
ex) return const_cast<char&>(static_cast<const Object&>(*this)[value]);
항목 4: 객체를 사용하기 전에 반드시 그 객체를 초기화 하자
C++은 초기화가 될 수도 안 될 수도 있다 --> 초기화 필수 (초기화 != 대입)
(C스타일의 malloc 사용으로 객체 초기화 안됨. new 사용)
(https://en.cppreference.com/w/cpp/language/initialization : 초기화 관련 문서 읽어보자)
대입문이 아닌 멤버 초기화 리스트를 쓰자
Object::Object(const char& _name): name(_name)
{
}
--> 대입은 초기화 후 값을 대입해서 두 번 실행되지만 초기화 리스트는 바로 필요한 값을 써버림. 효율적
(복사생성자와 대입연산자 개념이 헷갈릴 수 있으므로 정확하게 구분해야함)
(객체 초기화시 복사생성자 초기화 리스트를 사용해 초기화할 것)
전역/static 객체는 불확실한 초기화 순서를 염두해두자
--> 초기화 순서가 정해져 있지 않기 때문에 함수 내에서 선언을 해서 인스턴스를 get 해야한다.
--> 또는 singleton pattern을 사용하자
*refer:
'Programming > C++' 카테고리의 다른 글
[Effective C++] Chapter 2. 생성자, 소멸자 및 대입 연산자(2) (0) | 2022.06.12 |
---|---|
[Effective C++] Chapter 2. 생성자, 소멸자 및 대입 연산자(1) (0) | 2022.06.12 |
[Visual Studio 2017] 단축키 모음 (0) | 2022.02.27 |
.a와 .so 라이브러리 (Dependency) (0) | 2022.02.26 |
Offset, 메모리 할당; ( #pragma pack | alignas | offsetof ) (0) | 2022.01.30 |