Home C++ The Cherno
Post
Cancel

C++ The Cherno

From cherno C++

  • https://www.youtube.com/playlist?list=PLlrATfBNZ98dudnM48yfGUldqGD0S4FFb/

e5

  • Project Configuration -> Configuration type -> .exe/ .dll/ .lib
  • C++ -> Optimization -> Disabled(debug mode) / Max Optimization(release mode)
  • code compile to .obj file, linker them to .exe
  • VS error list is garbage. Use Output window.
  • Declaration VS Definition. Linker wiil find wrong declaration without definition when build the project.

e6 compiler

  • file name has no meaning, unlike java, just feed to compiler as .cpp or .h
  • include cpp files, after compiling, just one .obj file. They are one translation unit.
  • EndBrace.h file only contain ‘}’, in a.cpp, #include “EndBrace.h”, will behave same as a ‘}’
  • Configuration -> C/C++ -> Preprocessor -> to a file “Yes”. can check .i file which is pre codes. This option suppresses producing .obj file.
  • #if 0, #endif, preprocessor will fade out the codes between them.
  • check #include from .i file, we see iostream codes before our code for 50610 lines.
  • Configuration -> C/C++ -> Output Files -> Assembler Output. We can see .obj as assembly codes in .asm file.

e7 linker

  • if compile to .exe, linker will check entry point, main function existence.
  • file log.h has a static function, when included by other .cpp files, like different version of the function. waste & ugly.
  • file log.h has a non-static function, when included by other .cpp files, cause link error as multiply defined.
  • static: variable/function cannot be used in other translation units.

e8

  • int, 4 byte, -2 to 2 billion
  • char 1-byte, short 2-byte, long 4-byte, long long 8-byte.
  • float 4-byte, double 8-byte

e9

e10

  • Java, C# do not have header files.
  • #include, #pragma, etc. are preprocess command. do before compile.
  • #pragma once. In case copy the header file multiple times to one translation unit (not the whole program).
  • gcc clang msvc, all support #pragma once.
  • header file in C standard library like stdlib.h; in CPP standard library like iostream, no extension .h

e11 debug

  • windows -> debug -> memory - memory 1
  • Address: &a. check what in a`s memory address.
  • In debug mode, non initialized variables will be filled with “cc cc cc cc”.

e12 if

  • debug at break point, right click -> Go To Disassembly.
  • if(a) in assembly, test eax(a), eax(a); je [out of if block]; if block code. Same as if(a==0) not ifcode, else ifcode

e13 vs setup

  • config macro check. little arrow -> edit -> Macros.

e14

e15

  • continue, go to next loop; break, go out of entire loop; return, return function.

e16 pointer

1
2
3
4
5
char* buffer = new char[8];  // on heap
memset(buffer, 0, 8);

char** ptr = &buffer;
delete[] buffer;

e17 reference

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int a = 5;
int& ref = a;
// ref and a are same thing, same address, like another name of a.

void IncrementRef(int& value) {
    value++;
} 
// value is a reference of the parameter, so the parameter can be increased, out of the function.
// less code than below.

void IncrementPointer(int* value) {
    (*value)++;  // dereference the pointer first, then increase the value.
} 
// increase the value of the pointer, same effect as using reference.

e18/19/21 class structs

  • function in class named method.
  • class default private, struct default public. Only difference.

e22 static

  • static in class/struct, only one place of memory, for all the objects to use.
  • extern keyword, look the variable/function from other translation units. But not for static variable/function in other translation units.
  • private to class -> static to translation unit.
  • try static first, then global.

e23 enum

e24 constructor

  • private constructor (overload) prevent users to use class in an unwanted way.
  • class Log. in public: Log() = delete; also prevent this way of construct class.

e25 destrutor

  • manually call may lead to call twice, if doing with memory, not good.

e26 inheritance

  • class Player : public Entity {};
  • public inheritance makes public members of the base class public in the derived class, and the protected members of the base class remain protected in the derived class.
  • protected inheritance: protected.
  • private inheritance : private.

e27 virtual function

  • Entity* e = new Entity(); // new return the address on heap.
  • player : entity. pointer to player -> function, will call entiry.function().
  • Parent* p = new Child(); p->testFunc(); without ‘virtual’, will call parent`s function.
  • virtual method table (VMT). ‘virtual’ before function name. call right derived class function.
  • in subclass, mark overwritten function ‘override’.
  • a little cost, someone may not use virtual function.

e28 pure virtual

  • pure virtual function is specified by placing “= 0” in its declaration.
  • virtual double getVolume() = 0; it is interface.

e29

  • private, protected, public

e30 array

  • read memory not array, in debug mode may cruch, in release mode may just read.
  • int* int_ptr = array; *(int_ptr + 2) = 6; // same as array[2] = 6; // int array just a int pointer.
  • (int *)((char)int_ptr + 8) = 6; // same as *(int_ptr + 2) = 6; 8 one-byte equals 2 4-byte
  • int* another = new int[5]; delete[] another; delete an array on heap.
  • int* ptr1 = new int; int* ptr2 = new int(20); delete ptr1; delete ptr2 delete pointer, compare delete array
  • delete is used for one single pointer and delete[] is used for deleting an array through a pointer.
  • int* a=new int[5]; int b[5]; sizeof(a) = 4/8(32/64bit compile) ; sizeof(b) = 20. // pointer to heap/stack array size difference.
  • type a is int; type b is int[5]
  • static const int num = 5; int a[num] // must be static const.
  • #include <array> std::array<int, 5> onearray; // this standard array, before is raw array

e31 string

  • char abb[4] = {‘a’,’a’, ‘a’, 0}; char abb[4] = {‘a’,’a’, ‘a’, ‘/0’};
  • char abb[4] = “ABC”;
  • #include
  • void PString(const std::string& str) {} // const: not modify, &: not make a new copy of string.

e32

  • const char* name = “abc”;
  • const wchar_t* name2 = L”abcdasdf”; // 2 byte for a char
  • const char16_t* name3 = u”asdf”; // 2 byte
  • const char32_t* name4 = U”pvawe”; // 4 byte
    1
    2
    3
    4
    
    const char* aa = R"(wef
    fewef
    fwef
    fwef)"; // change line easily, R means raw.
    

e33 const

  • const int* a = new int; // a=(int *)&anotherInt is good ; *a = 2 cannot do. cant change content
  • const int a, same as, int const a
  • int* const a = new int; // a=(int *)&anotherInt cannot do ; *a = 2 is good . cant change address
  • const before/after *
  • const int* const a = new int // cannot change content and address
  • in class public function, int GetX() const. const function will not change class. Like Read Only.
  • void function(const EntityClass& e){} // can only call const function of the class.
  • mutable int a; // in class, can be modified in const function.

e34 mutable

  • auto f = ={}; // lambda function, = pass all variable by value, & all by reference
    1
    2
    3
    4
    5
    6
    7
    8
    
    int x = 8;
    auto f = [=]() mutable
    {
      x++;
      std::cout<<x;
    } // pass by value can used right away; or int y=x, then use y
    f();
    // just another usecase of mutable, 99% just in const function of class, not this.
    

e35

  • initialize class.
  • Entity() : m_Name(“Unknown”), m_Score(0) {} // same as below
  • Entity() { m_Name = “Unknown”; m_Score = 0; } // same as above
  • Entity(const std::string& name) : m_Name(name) {} // another example

e36 Ternary

  • Speed = Level > 5 ? 10 : 5;

e37

  • using namespace std; // is not not not recommended.
  • Entity* entity = new Entity();
  • entity->function(); or (*entiry).function(); // pointer -> , dereference .

e38 new

  • new on heap
  • right click ‘new’ -> go to definition. new is an operator.
  • void* __CRTDECL operator new(size_t _Size);
  • Entity* e = new Entiry(); // same as below, but call constructor.
  • Entity* e = (Entity*)malloc(sizeof(Entity)); // same as above, but not call constructor.
  • delete e; // when use new, must delete.
  • Entity* e = new(ptr) Entiry(); // new to a specific pointer.

e39

  • explicit constructor, stricter rule

e40 operator overloading

1
2
3
4
5
6
7
8
9
10
11
Vector2 operator+(const Vector2& other) const{
	return Vector2(x+other.x, y+other.y);
}// overload + 

Verctor2 Add(const Vector2& other) const{
	return *this + other; 
}// this is a pointer to the object, *this is Vector2

Verctor2 Add(const Vector2& other) const{
	return operator+(other);
} // same as above, but werid.

overload std::cout to print class Vector2

1
2
3
4
std::ostream& operator<<(std::ostream& stream, cont Vector2& other){
	stream << other.x << ", " << other.y;
	return stream;
}

e41 this

  • pointer to the current object instance that the method(non-static) belongs to.

e42

  • allocate object on stack, will destroy when code out of scope. On heap, it is not. ```c++ class ScopedPtr{ private: Entity* m_Ptr; public: ScopedPtr(Entity* ptr): m_Ptr(ptr) {}

    ~ScopedPtr() { delete m_Ptr; } }

int main() { { ScopedPtr e = new Entity(); } }

// new on heap will not auto free memory when out of scope, // but with ScopedPtr, the pointer on stack will free the memory on heap by its destructor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
e42 smart pointer
- call new on heap, do not need to delete.
- wrapper around a raw pointer.
- can not copy a unique pointer, one auto delete will affect the other.
```c++
#include <memory>
{
	{ std::unique_ptr<Entity> entity = std::make_unique<Entity>(); } // best safe way 
	// { std::unique_ptr<Entity> entity(new Entitry()); }  another way to initialize.
	// can not use = new Entitry(); to initialize, because explicit keywork in unique_ptr.
	// entity will be destroyed when out of scope.
}

//in unique_ptr classs
unique_ptr(const unique_ptr&)            = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
// in case copy a unique pointer.

shared pointer

  • std::unique_ptr, std::shared_ptr, std::weak_ptr
  • need reference counter, count how many instances have created.
  • memory freed when last pointer dies.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    int main(){
      {	
      std::shared_ptr<Entity> e0;
          {
              std::shared_ptr<Entiry> sharedEntity = std::make_shared<Entity>();
              e0 = sharedEntiry; 
          }  // heap not freed here. e0 still exists.
      } // heap freed here, e0 out of scope.
    }
    

weak pointer

  • do not count the instances, act as an observer, use after asking if exists.
    1
    2
    3
    4
    
    std::weak_ptr<int> gw;
    gw.use_count();
    if (std::shared_ptr<int> spt = gw.lock()) {} // no object, lock return empty, so false
    if (gw.expired()) {} // Equivalent to use_count() == 0
    

e44

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class String{
private:
	char* m_Buffer;
	unsigned int m_Size;
public:
	String(const char* string){
		m_Size = strlen(string);
		m_Buffer = new char[m_Size+1]; // 1 for null termination character.
		memcpy(m_Buffer, string, m_Size); // m_Size in bytes.
		m_Buffer[m_Size] = 0;
	}
	
	~String(){
		delete[] m_Buffer;	
	}
	
	friend std::ostream& operator<<(std::ostream& stream, const String& string);
	// this allow ostream << operator to get String class private value m_Buffer.
};

std::ostream& operator<<(std::ostream& stream, const String& string){
	stream << string.m_Buffer;
	return stream;
}

int main(){
	String a = "abc";
	String b = a;
	// a is a reference, b just copied all variable from a. shallow copy.
	// copy int value and memory address.
	// end of scope, it will destruct twice, cause program to crash.
}

copy constructor

1
2
3
4
5
6
7
8
String(const String& other)
{
	m_Buffer = new char[m_Size + 1];
	memcoy(this, &other, sizeof(other)); //deep copy
	// *this.buffer = other.buffer; *this.size=other.size shallow copy
}
// default will be shallow copy.
// make your version of how to copy.

e45

1
2
int offset = (int)&((Vector3*)nullptr)->y;
// check offset of a class.

e46 std::vector

  • dynamic array. opposite to raw array, size fixed. ```c++ #include

std::vector mystruct; mystruct.push_back({1,2,3}); // {1,2,3} initialize a new mystruct. int i = mystruct.size();

1
2
3
4
5
6
7
8
9
10
11
e47 vector push_back copy times
- test how many times a class has been copied.
- log in copy constructor.
```c++
std::vector<Vertex> vertices;
vertices.push_back(Vertex(1,2,3)); // copy *1, total 1. copy from main stack, to heap.
vertices.push_back(Vertex(4,5,6)); // copy *2, total 3. resize, then on heap.
vertices.push_back(Vertex(7,8,9)); // copy *3, total 6. resize, then on heap.

vertices.reserve(3); // use before push_back
  • ALWAYS try to reserve before you start pushing back elements into the container.
    1
    2
    3
    4
    5
    6
    7
    
    // no single copy version
    std::vector<Vertex> vertices;
    vertices.reserve(3);
    vertices.emplace_back(1,2,3);
    vertices.emplace_back(4,5,6);
    vertices.emplace_back(7,8,9);
    // total 0 copy !!!
    

e48 static in scope

1
2
3
4
5
6
void function(){
	static int i = 0;
	i++;  
	std::cout << i << std::endl;
}
// output: 1,2,3,.....

singleton by static, easy and clean

1
2
3
4
5
6
7
class Singleton(){
public:
	static Singleton& get(){
		static Singleton instance; // first time will create it on stack, later will not.
		return instance;	
	}
};

e49

  • glfw3.dll dynamic library. glfw3dll.lib static library use with .dll. link at compile time.
  • glfw3.lib static library
  • config -> c/c++ -> Genral -> Additional Include Directories. for header.
  • config -> Linker -> Input -> Additional Dependencies. for lib file name.
  • config -> Linker -> General -> Additional Library Directories. for lib path.
1
2
3
extern "C" int glfwInit(); 
// glfw is a C library, without extern “C” will mangling the name with C++.
// preserve the name even though it is in C.

e50 dll

  • use when should use. runtime.
  • config -> Linker -> Input -> Additional Dependencies. add glfw3dll.lib. this help find .dll function address.
  • complie and run. get error. can not find glfw3.dll.
  • simple way: copy .dll to .exe, in same path.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* GLFWAPI is used to declare public API functions for export
 * from the DLL / shared library / dynamic library.
 */
#if defined(_WIN32) && defined(_GLFW_BUILD_DLL)
 /* We are building GLFW as a Win32 DLL */
 #define GLFWAPI __declspec(dllexport)
#elif defined(_WIN32) && defined(GLFW_DLL)
 /* We are calling a GLFW Win32 DLL */
 #define GLFWAPI __declspec(dllimport)
#elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL)
 /* We are building GLFW as a Unix shared library */
 #define GLFWAPI __attribute__((visibility("default")))
#else
 #define GLFWAPI
#endif

e51

  • more projects in one solution.
  • right click one project -> add -> reference -> another project.
  • complie A and B, if A rely on B.

e52

  • function parsing pointer than reference, good thing is null can be parsed.
  • array, vector difference. Just assign value(without new): array data on stack, vector data on heap.
  • tuple, struct.

e53 templates

  • compiler write code for you. ```c++ template void function(T value){ // do somethin.} function(5); function("afsdf");

// if define class name template function(5); function<std::string>("aasdf");

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- If not call templeta function, compiler will ignore it, even with sytax error.
- template only create code when we use the function.
```c++
template<typename T, int N>

class Array{
private:
	T m_Array[N];
public:
	int GetSize() const {return N;}
};

// in main
Array<int, 5> array;
// in compile time, the array class will be coded as m_Array[5]

e54

  • new allocate on heap.
  • delete value, delete[] array; after using new.
  • new will call malloc function, which ask free list, for free memory to allocate.
  • allocate on stack is one CPU instruction; while on heap, a series of instructions.
  • stack: mov DWORD PTR _value$[ebp], 5 ; one instruction

e55 macro

  • find and replace
  • #define WAIT std::cin.get()
  • debug mode need more logs.
  • Indebug mode: C/C++ -> Preprocessor -> Preprocessor Definitions: add PR_DEBUG in the list.
  • same as above in release mode, add PR_RELEASE. ```c++ #ifdef PR_DEBUG #define LOG(x) std::cout « x « std::endl #elif defined(PR_RELEASE) #define LOG(x) #endif

// if in debug mode, LOG(x) will print; in other mode, LOG(x) do nothing

// another way #define PR_DEBUG 1 #if PR_DEBUG == 1 // same as above, maybe a little bit clear

// like comment out all macro, see below #if 0 // all the marco you want to comment out here. #endif

1
2
3
4
5
6
7
8
e58/59 function pointer, lambda
```c++
void fun(int a){}

void (*fun_ptr)(int) = &fun;

auto x1 = [](int i){ return i; }; // lambda

e62 threads

1
2
3
4
5
6
7
8
9
10
11
12
#include <thread>

void DoWork() {
	using namespace std::literals::chrono_literals;  // use 1s later
	std::cout << std::this_thread::get_id();   // print thread ID.
	std::this_thread::sleep_for(1s);   		 	 // sleep for 1 second.
}

int main(){
	std::thread worker(DoWork);
	work.join(); // main thread wait for worker thread to finish.
}

e63

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <chrono>

auto start = std::chrono::high_resolution_clock::now();
std::chrono::duration<float> duration = end - start;
std::cout << duration.count();

////////////////////////////////////

struct Timer{
	std::chrono::time_point<std::chrono::steady_clock> start,end;
	std::chrono::duration<float> duration;
	
	Timer(){
		start = std::chrono::high_resolution_clock::now();	
	}
	
	~Timer(){
		end = std::chrono::high_resolution_clock::now();
		duration = end - start;
		float ms = duration.count() * 1000.0f;
		std::cout << Timer took << ms << "ms" << std::endl;	
	}
};

// test the run time of code block, just put 
// Timer timer;
// in the beginning of the code block.

e65 sorting

1
2
3
4
std::vector<int> values = {1,3,4,6,5};
std::sort(values.begin(), values,end(), [](int a, int b){
	return a<b;
});

e66 type punning

1
2
3
Entity e = {5, 8};
int* position = (int*)&e;
int y = *(int*)((char*)&e+4);  // 4 char is 4 byte, to the next int y, then dereference.

e67

1
2
3
4
5
6
7
8
9
10
11
struct Vector4{
	union{
		struct{
			float x,y,z,w;		
		};
		struct{
			vector2 a,b;
		};
	}
};
// using vector4.z, you can set vector2.b.x

e68 virtual destructor

  • like virtual function.
    1
    2
    3
    4
    5
    6
    7
    
    Base* poly = new Derived();
    delete poly;
    // ~Derived() will not be called.
    // only call: Base constructor, Derived constructor, Base Destructor.
    // if free memory action in ~Derived(), it will not be called.
    // in Base class, add virtual keyword before ~Base(){}, the ~Derived() will be called.
    // order will be: base cons, derived cons, base destructor, derived destructor.
    

e69 casting

  • static_cast, dynamic_cast, reinterpret_cast, explicit cast, const_cast
  • static_cast: no run-time test, use when know passed object type.
  • dynamic_cast: don’t know what the dynamic type of the object is. bad_cast exception. can not down casting. safe.
1
2
3
4
5
6
7
DerivedClass * a = dynamic_cast<DerivedClass*>(baseObject);
// down casting, return nullptr.

Player* p = dynamic_cast<Player*>(actuallyEnemy);
// run time error.
if(dynamic_cast<Player*>(actuallyEnemy)) {}
// like C# is, Java instanceof().  If actuallyEnemy is a Player.

e70 condition & actions

  • set condition in runtime
  • use condition & action together
  • debug easily

e71

  • precompiled headers. PCH.

e74 benchmark

  • ref e63
  • use release mode

e75 structured binding

1
2
3
4
5
6
7
8
std::tuple<std::string, int> CreatePerson() {return{"name", 15};}
std::string name = std::get<0>(person); // get the first element of tuple

// another way
std::tie(name, age) = CreatePerson();

// another way, cpp version 17 feature
auto[name, age] = CreatePerson();

e76 optional data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
std::optional<std::string> ReadFileAsString(const std::string& filepath){
	std::ifstream stream(filepath);
	if(stream){
		std::string result;
		stream.close();
		return result;	
	}
	
	return {};
}


std::optional<std::string> data = ReadFileAsString("data.txt");
if(data.has_value()){} // check if data is empty
}
// nicer way to handle if value not be present.

e77

1
2
3
std::variant<std::string, int> data; // can save 2 different types of data
data.index() // save string will be 0, in will be 1.
auto* value = std::get_if<std::string>(&data);
This post is licensed under CC BY 4.0 by the author.