C++ 메모
Sources✔
-
The C++ Primer
Effective C++
More Effective C++
Effective STL
EFFECTIVE MODERN C++
The C++ Standard Library : A Tutorial and Reference
C++ Templates: The Complete Guide
-
"C++ Programming Language" by Bjarne Stroustrup
-
https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list
-
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
memo✔
-
변수 초기화 시
=를 사용하면 narrow type conversion(double→int→char) 시 데이터가 유실됨. 그러나{}를 사용하면 데이터가 유실될 때 에러를 발생시켜줌. 따라서{}로 변수 초기화하는 게 더 좋음.그러나 특별한 상황이 아닌 경우 타입형으로
auto를 사용함. 특별한 상황이란 코드 독자에게 타입형이 명확하게 읽혀야만 하는 경우, 또는 변수의 정확도를 명확하게 지정해야 하는 경우(float가 아닌double) -
constexpr: 런타임 때 계산되는 게 아니라 컴파일 때 계산. 변수, 함수에 사용 가능. 성능 향상이 됨.const는 런타임에 계산 가능한 상수를 의미함. -
&를 변수에 사용하면 주솟값을 반환할 때 사용되고, 타입형에 사용하면 레퍼런스를 얻을 때 사용됨. 레퍼런스는 일반 파라미터나 포인터와 달리 해당 변수의 값을 직접 수정 가능.레퍼런스가 포인터에 비해 나은 점은 포인터는 데이터에 접근할 때
*를 써야 하는데 레퍼런스는 필요없음.파라미터로 전달된 값이 수정되지 않아야 하는 경우에도 레퍼런스를 사용한다면, 파라미터로 값을 복사하는 비용이 사라지기 때문에
const T&와 같은 식으로 사용하면 성능 향상이 이루어짐. -
if (auto n = v.size(); n!=0)와 같이 if 문에서 변수를 초기화할 수 있다. 이 변수n을 if문 안에서 사용할 수 있다. 이 if문을 더 단순하게if (auto n = v.size())로 줄일 수 있다.n이 0 이 아닌지 검사한다. -
이 팁들은 C++ Core Guidelines 의 부분집합이다.
-
가능한한 원시 타입 보다 유저 정의 타입(struct, class, enum 등)을 사용하는 게 더 좋다. 그것이 사용하기 더 쉽고, 에러가 적고, 개발자에게 주어진 일에 대부분 더 적합하고, 심지어 더 빠르다.
-
struct 사용법
-
위와 같이
new는 힙 메모리(동적 메모리)를 할당해주며, 이 메모리는delete로 제거할 때까지 소멸되지 않는다. -
struct멤버에 접근할 때.으로 접근하면 되고, 포인터struct의 멤버에 접근할 때는->로 접근하면 된다. 그러나 C++ 에는 레퍼런스를 보통 사용하므로->는 C 언어에서의 레거시인듯. -
레퍼런스/포인터 동작 이해하는 예시:
int x = 2; int y = 3; int∗ p = &x; int∗ q = &y; // now p!=q and *p!=*q p = q; // p becomes &y; now p==q, so (obviously)*p == *q
int x = 2; int y = 3; int& r = x; // r refers to x int& r2 = y; // now r2 refers to y r = r2; // read through r2, write through r: x becomes 3
-
class 사용법
class Vector { public: Vector(int s) :elem{new double[s]}, sz{s} { } // constr uct a Vector double& operator[](int i) { return elem[i]; } // element access: subscripting int size() { return sz; } private: double∗ elem; // pointer to the elements int sz; // the number of elements };생성자는 초기화되지 않은 멤버 변수들을 제거하는 역할을 한다. 즉, 클래스의 모든 변수를 초기화해야 하는 의무를 갖는다.
본질적으로는, struct 와 class 는 서로 같다. struct 는 단지 public 이 디폴트인 class 일 뿐이고, struct 를 위한 생성자나 멤버 함수를 외부에 정의할 수 있기 때문.
-
union 이란 모든 멤버들이 같은 주소에 할당되는 struct 이다. 즉, union 은 가장 큰 공간을 차지하는 멤버 변수의 공간을 할당 받는다. union 이 사용되는 상황은 멤버 변수 중에서 오직 하나만 사용하는 경우이다. 이 경우 struct 를 사용하면, 나머지 변수를 사용하지 않기 때문에 공간이 낭비된다. 따라서 union 을 사용한다.
enum Type { ptr, num }; // a Type can hold values ptr and num (§2.5) struct Entry { string name; // str ing is a standard-librar y type Type t; Node∗ p; // use p if t==ptr int i; // use i if t==num }; void f(Entry∗ pe) { if (pe−>t == num) cout << pe−>i; // ... }이 경우 p 와 i 가 동시에 사용되지 않는다. 따라서 다음과 같이 하면 좋다.
-
위의 Union 예제에서 type 을 의미하는
Type t와 union 이 실제로 갖고 있는 타입 사이의 대응관계가 올바르도록 유지하는 과정에서 에러가 발생하기 쉽다. 이 에러를 피하기 위해 보통은, 로우레벨에서 계속해서 코딩하지 않고, union 과 type 사이의 대응관계를 class 에서 캡슐화하고 그것에 접근하는 멤버 함수를 만들어두어서 union 을 올바르게 사용하도록 한다. 실제로 union 을 직접 다루는 것은 지양해야 하고, 최소화되어야 한다.이 경우
variant라는 STL 타입을 사용하여 union 같은 로우레벨 인터페이스를 직접 다루는 것을 피하는 것이 더 낫다. -
enum 타입은 다음과 같이 사용될 수 있다.
enum class Color { red, blue , green }; enum class Traffic_light { green, yellow, red }; Color col = Color::red; Traffic_light light = Traffic_light::red;enum 은 정수형 값의 작은 집합을 표현하는데에 좋다. enum 을 자주 사용하면 코드 가독성이 높아지고, 에러가 감소하여 좋다.
enum class 를 사용하면 enum 이 강하게 타입화되고 그것의 enumerator 들이 스코프 안에 놓이게 된다. 즉,
Color::red와 같이 사용해야 한다. 이로써Traffic_light::red와 같은 전혀 다른 context 에 있는 상수인red라는 상수를 잘못 사용하는 경우가 사라진다.enum class 가 enum 보다 더 좋다.
-
C++ 은 함수, 사용자 정의 타입, 클래스, 템플릿으로 모듈화된다.
-
다음과 같이 선언을 하고, 구현을 따로 한다.
double sqrt(double); // the square root function takes a double and returns a double class Vector { public: Vector(int s); double& operator[](int i); int size(); private: double∗ elem; // elem points to an array of sz doubles int sz; };그리고 어딘가에서 다음과 같이 구현하면 된다.
double sqrt(double d) // definition of sqrt() { // ... algorithm as found in math textbook ... } Vector::Vector(int s) // definition of the constructor :elem{new double[s]}, sz{s} // initialize members { } double& Vector::operator[](int i) // definition of subscripting { return elem[i]; } int Vector::siz e() // definition of size() { return sz; } -
다음과 같이 모듈화될 수 있다.




그러나 이 형식은 컴파일 비용이 높고 에러가 쉽게 발생하는 C언어로부터의 레거시이다. C++20 이상에서는
module과import를 통하여 더욱 현대적으로 모듈화가 진행된다. -
namespace 를 사용하면 선언들을 묶을 수 있고, 다른 이름과 충돌되지 않게 할 수 있다.
-
throw키워드는 실행흐름을 예외 핸들러로 넘겨준다.try{ } catch() { }문을 사용하면 에러를 핸들링할 수 있다. -
파라미터를 전달하는 기본 방식은 복사다. 그러나 복사는 비효율적이다. 따라서 파라미터를 reference 로 전달하는 것이 효율이다. 이때 const-reference 로 파라미터를 전하면 원래 값이 불변한다는 것이 보장된다.
그런데 성능을 좀 더 엄밀하게 향상시키려면 작은 값은 그냥 전달하고, 큰 값만 reference 로 전달하면 좋다. "작은" 의 정의는 복사해도 상관없을만큼 비용이 매우 작은 작은 값이라는 의미이다. 그래서 이 의미는 컴퓨터에 따라 달라지지만, 보통은 둘 또는 셋의 포인터들의 사이즈 혹은 그보다 더 작은 값이라고 보면 된다.
함수 반환값의 기본 방식도 복사이다. 물론 작은 값에 대해서 복사해도 좋다. 만약 함수의 로컬에 존재하지 않는 객체에 대한 접근을 반환하고 싶다면 reference 를 반환하면 된다. 큰 객체에 대한 포인터를 반환하는 것은 구식이고, 에러가 발생하기 쉽다.
-
큰 객체를 리턴할때 복사하지말고 move 라는 생성자를 사용하면된다
- 반환타입을 auto 로 정의할 수 있다. 그러나 인터페이스가 불안정해진다.
-
struct 반환 예시
struct Entry { string name; int value; }; Entr y read_entr y(istream& is) // naive read function (for a better version, see §10.5) { string s; int i; is >> s >> i; return {s,i}; } auto e = read_entry(cin); cout << "{ " << e.name << " , " << e.value << " }\n"; -
생성자에
~를 붙이면 소멸자를 선언할 수 있다.class Vector { public: Vector(int s) :elem{new double[s]}, sz{s} // constr uctor: acquire resources { for (int i=0; i!=s; ++i) // initialize elements elem[i]=0; } ˜Vector() { delete[] elem; } // destr uctor: release resources double& operator[](int i); int size() const; private: double∗ elem; // elem points to an array of sz doubles int sz; };클래스는 일반 변수처럼 소멸된다.