C++の文法について、個人的なメモ
Objectをinstantiateする時、メモリをstackとheapどちらからallocateするかによって処理内容と文法が大きく違います。
struct Vector3 {
float x, y, z;
Vector3()
: x(10), y(11), z(12) {}
};
// stack
// int
int value = 5;
// array. allocating 5 * int size in memory
int array[5];
array[0] = 1;
array[1] = 2;
array[2] = 3;
array[3] = 4;
array[4] = 5;
// object
Vector3 vector;
// heap
// int
int* hvalue = new int;
*int = 5;
// array
int* harray = new int[5];
harray[0] = 1;
harray[1] = 2;
harray[2] = 3;
harray[3] = 4;
harray[4] = 5;
// object, () is optional
Vector3* hvector = new Vector3();
// you need to manually call delete to deallocate
delete hvalue;
delete[] harray;
delete hvector;
stackにvariableをallocateする時は、”stack pointer”がメモリ上でvariableのサイズだけ移動させ、メモリを次々とallocateする(stack)ので、動作がとても早いです。variableのscopeが終わると自動的にdeallocateされます。
heapの場合はallocateの時newで「free list(メモリ領域にあるavailableなメモリのリスト)」からavailableなメモリブロックを探しだし、確保し、そのpointerをdereferenceした上で、値を代入します。また、明示的に delete(deallocate)する必要があります。もしApplicationに割り当てられた以上のメモリが必要になると、Applicationはsystemに要求します。つまりとても「コストが高い」オペレーションです。
smart pointers の場合は裏でこのnew、deleteが呼ばれています。
可能な限りstackにメモリをallocateする方が良い、ということです。
int a = 5;
int* ptr = &a;
std::cout << *a << std::endl; // prints 5 (value of a)
int& ref = a; // ref is now an alias of a
ref = 2;
std::cout << a << std::endl; // prints 2
このThe Chernoのtutorialで説明されているように、initializer listによるinitializationは、member variableを一度だけinstantiateするため効率が良いです。また、initializer listによるinitializationはinitializer listの順番ではなく、member variableが宣言されている順に実行されるため、dependencies(どのmemberが先にinitializeされなくてはならないか、など)に関して気をつけなければなりません。
class Entity {
public:
// a constructor without parameter
Entity {
}
// a constructor with initializer lists
Entity(float x, float y)
: X(x), Y(y) // it needs to be in the same order as you declared
{
}
// a constructor with parameters
Entity(float x, float y) {
X = x;
Y = y;
}
float X, Y;
}
class Log {
public:
// by assigning delete, can disable default constructor
Log() = delete;
}
// example of deep copy...
class Entity {
public:
Entity() {} // default constructor
// copy constructor
Entity(const Entity& other) {
m_X = other.m_X;
}
int m_X;
}
//unique pointer
// shared pointer
// weak pointer
Macro
// example
#define LOG(x) std::cout << x << std::endl
// example...
using MidiKeyboardStateListener = MidiKeyboardState::Listener;
const int MAX_AGE = 90;
int* a = new int; // a is a pointer
*a = 2; // you can dereference a and assign 2
a = (int*)&MAX_AGE // you can reassign pointer
const int* a = new int; // add "const" keyword to a pointer
// you can also write it as "int const* a"
*a = 2; // error, you can't modify the contents of the pointer
a = (int*)&MAX_AGE; // no error
std::cout << *a << std::endl; // reading a works
int* const a = new int;
*a = 2; // this works
a = (int*)&MAX_AGE; // error
a = nullptr; // error
class Entity {
private:
int m_X, m_Y;
public:
int GetX() const { // it means the function can't modify the value
// read only method
// good for getters
m_X = 2; // can't do this
return m_X;
}
void SetX(int x) { // can't have const here...
m_X = x;
}
}
void PrintEntity(const Entity& e) {
std::cout << e.GetX() << std::end; // if I remove const from
// the above GetX(), it won't work
}
lvalueはメモリ上にアドレスがある値(変数など)j
rvalueはメモリ上にアドレスがない値(記述されただけの値)
int i = 5;
int& a = 10; // this does not work, because 10 is a rvalue
int& b = i; // this works, because i is a lvalue
const int& c = 10; // this works
// it creates a temporary behind the scenes as in the below pseudo code
int temp = 10;
int& c = temp;
Lambda — captureをthis(オブジェクト)でするか、&(オブジェクトのreference)でするか、ですが、referenceの場合はmember変数に変更を加えます。
関数の戻り値へconst keywordをつけると、戻り値はconst(変更不可)になります。
// example
juce::String getCurrentPreset() const;