- 虚函数
- B是A的子类,A中有虚函数a,
class Entity { public: <!-- 这里加上virtual才能实现多态,否则(Entity *)player->getName()会调用Entity的方法 --> virtual std::string GetName() { return "Entity"; } }; class Player : public Entity
- 缺点:
- 需要额外的内存空间存储虚函数的表,使得指向基类V表中的参数将正确的指针发送给函数。
- 每当调用虚函数,需要遍历虚函数表查找它映射了哪个函数
- 纯虚函数
- B是A的子类,A中有虚函数a,
- 可见性
- see,use
- private(只有自己和friend可以访问), protected(只有自己和派生类可以访问,实体类都无法访问), public()
- 为了人类自己的方便而定义的
class Entity{ //private int x, y;}; struct Entity{ //public int x, y;};
- 数组
- 数组的名字实际上是一个指针
- &e 可以用这个在visual studio 的Memory中查找地址
static const int size = 5; int example[size]; 不能 example.size(); // C++ 11 新规范 std::array<int, 5> another; 可以 another.size();
- 字符串
- 字符是什么?一个byte的内存,UTF-8,256种,UTF-16,16个位
- 字符串以 00 结尾,指示该字符串结束
std::string name = std::string("yuqin") + "hello"; <!-- 或者下面两行这样 s 表示一个函数 --> using namespace std::string_literals; std::string name0 = "Cherno"s + "hello"; bool contains = name.find("no") != std::string::npos;
- 当传入一个字符串作为参数的时候,是将字符串进行拷贝
- 字符串面量
<!-- 只能这样使用char *表示字符串 --> char *name = (char*)"Che\0rno"; const char name[8] = "Che\0rno"; std::cout << strlen(name) << std::endl; <!-- 这样加R可以换行 --> const char* example = R"(Line1 Line2 Line3)";
- cosnt
- const位置
int* const a = new int; // 可以改变指针内容,但是不能改变自己 const int* a = new int; // 不能改变指针内容,但是能改变自己 // 返回一个不能改变的指针,指针的内容不能被改变,这个方法不能改变Entity实体类 const int* const Getx() const { return _x; } int* _x, _y; // 只有_x是指针, // 这个不加cosnt无法使用 void PrintEntity(const Entity& e) { int Getx() const { return _x; } void PrintEntity(const Entity& e) { std::cout << e.Getx() << std::endl; } <!-- mutable定义的变量可以在const函数中被改变 --> mutable int var; int Getx() const { var = 2; }
- const位置
- mutable (少见)
int x = 8; // lambda auto f = [=]() mutable{ x++; // 创建一个新的变量, std::cout << x << std::endl; }; f();
- 类初始化列表
Entity() : m_Name("unKnown"), m_Score(0) { } <!-- 普通创建example会有两次 --> class Example { public: Example() { cout << "create example! " << endl; } Example(int x) { cout << "create example " << x << " !" << endl; } }; class Entity { private: Example example; public: Entity() { example = Example(8); /*create example! create example 8 !*/ } <!-- 换成初始化列表只会生成一次 --> Entity(): example(8) { /*create example 8 !*/ } }; };
- Ternary operator
- 三目运算符,替代 if else。
- 创建一个对象
- Entity e; 这样就是一个初始化的合法的代码,不像java需要new。
Entity e = Entity("Cherno"); <!-- 生命周期,{}语句块结束之后e仍然指向那个相同地址,但是"cherno"已经被销毁 --> Entity *e; { //在stack上分配 Entity entity("Cherno"); e = &entity; cout << entity.GetName() << endl; } <!-- new分配heap上的内存 --> // new 非常重要,在heap上分配内存,耗时,不会自动回收 Entity* entity = new Entity("Cherno"); delete entity; <!-- 不会自动销毁 --> Entity *e; { // new 非常重要,在heap上分配内存 Entity* entity = new Entity("Cherno"); cout << (*entity).GetName() << endl; }
- Entity e; 这样就是一个初始化的合法的代码,不像java需要new。
- new
- 内存、性能、优化
- new在heap上分配内存;
int a = 2; int* b = new int; //4bytes; int* c = new int[50]; // 200bytes delete b; delete[] c; <!-- 相同 --> Entity* e = new Entity(); Entity* e = (Entity*)malloc(sizeof(Entity)); <!-- 指定new的分配地址 --> int* c = new int[50]; // 200bytes Entity* e = new(c) Entity(); //假设Entity有60字节?指定Entity分配的内存地址
- 隐式转换和explicit关键字
<!-- 隐式转换 --> Entity(const string& name) : m_Name(name) {} Entity(int age) :m_Name("Unknown"), m_Age(age) {} Entity a = string("Cherno"); // 可以运行但不建议 Entity b = 22; //可以运行但不建议 Entity b(22); //建议 <!-- explicit强制必须是该类的构造,而不是隐式转换 --> explicit Entity(int age) :m_Name("Unknown"), m_Age(age) {} Entity b = 22; // 不行,错误 Entity b = (Entity)22; //可以
- 操作符
- 重载
<!-- 重载 << 操作符 --> ostream& operator << (ostream& stream, const Vector2& other) { stream << other.x << "," << other.y; return stream; <!-- 重载, +操作符 --> Vector2 operator+(const Vector2& other) const { return Vector2(x + other.x, y + other.y); } Vector2 result2 = position + speed;
- 重载
- this
- 对象的生命周期
- 作用域对栈中的对象有用,New出来的对象在heap上,不受作用域限制
- 智能指针的启发:
class Entity { public : int x, y; Entity(){ cout << "create" << endl; } ~Entity() { cout << "destoryed" << endl; } }; class ScopePtr { private: Entity * m_Ptr; public: ScopePtr(Entity* ptr) :m_Ptr(ptr) { } ~ScopePtr() { delete m_Ptr; ; } }; int main() { { ScopePtr e = new Entity(); }
- 智能指针
// unique std::unique_ptr<Entity> e0 = std::make_unique<Entity>(); std::unique_ptr<Entity> e1 = e0; // 非法 <!-- shared --> { std::shared_ptr<Entity> e0; { std::shared_ptr<Entity> shar_ptr = make_shared<Entity>(); e0 = shar_ptr; } //如果是weak_ptr,则在这里销毁 } //这个结束Entity才销毁
- 复制和复制构造函数
- =就是复制 ```C++ struct Vector2 { float x, y; }; Vector2 a = { 2,3 }; Vector2 b = a; b.x = 5; // a {2,3} ; b{5,3} 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]; memcpy(m_Buffer, string, m_Size); m_Buffer[m_Size] = 0; } // 深拷贝 String(const String& other) : m_Size(other.m_Size){ m_Buffer = new char[m_Size + 1]; memcpy(m_Buffer, other.m_Buffer, m_Size + 1); } ~String() { delete[] m_Buffer; } char& operator { return m_Buffer[index]; } friend std::ostream& operator«(std::ostream & stream, const String& string); };
std::ostream& operator«(std::ostream & stream, const String& string) { stream « string.m_Buffer; return stream; } struct Vector2 { float x, y; }; int main() { String string = “yuqin”; String second = string; second[2] = ‘a’; cout « string « endl; cout « second « endl;
std::cin.get(); } ```
- 箭头符号
- 自动delete代理。//ziji:代理这个词用得不错 ```C++ class Entity { private: int x; public: void Print() const { cout « “Hello “ « endl; } };
class ScopedPtr { private: Entity* m_Obj; public: ScopedPtr(Entity* entity) :m_Obj(entity){
} ~ScopedPtr() { delete m_Obj; } Entity* GetObject() { return m_Obj; } <!-- 第二种方法,重载箭头 --> const Entity* operator->() const{ return m_Obj; } }; int main() { ScopedPtr entity = new Entity(); entity.GetObject()->Print(); <!-- 第二种方法 --> const ScopedPtr entity = new Entity(); entity->Print(); std::cin.get(); } <!-- 利用->找到偏移值 --> struct Entity { int x, y, z; }; int main() { int offset = (int)&((Entity*)0)->x; cout << offset << endl; std::cin.get(); } ```
- 动态数组,vector
- stl
- 如何优化使用vector
- 首先再main函数得stack中构建一个vector,然后需要拷贝到类的空间中,
<!-- 拷贝3次 --> vector<Vertex> vertices; vertices.reserve(3); vertices.push_back(Vertex{ 1,2,3 }); vertices.push_back(Vertex{ 4,5,6 }); vertices.push_back(Vertex(7, 8, 9)); <!-- 使用emplace_back直接在vertex的堆上生成 --> vector<Vertex> vertices; vertices.reserve(3); vertices.emplace_back(1,2,3 ); vertices.emplace_back( 4,5,6 ); vertices.emplace_back(7, 8, 9);
- 首先再main函数得stack中构建一个vector,然后需要拷贝到类的空间中,
- 库
- GLFW,include,library
- 第一,在C++的general中设置additional include directories为项目相对路径下的glfw的include文件夹,第二,在Linker的general中设置additional library directories文件夹,并设置input中的additional dependencies.
#include <GLFW/glfw3.h> using namespace std; int main() { int a = glfwInit(); cout << a << endl; std::cin.get(); }
- 返回多个不同种类的值
- 使用struct,带引用的参数
- template
template<typename T> void Print(T value) { cout << value << endl; } Print(5); // C++自动知道是int类型 Print<string>("hello"); <!-- 利用template初始化数组 --> template<typename T, int N> class Array { private: T m_Array[N]; public: int GetSize() const { return N; } }; Array<int,5> array; cout << array.GetSize() << endl;
- ziji:感觉C++非常灵活。
- 在Log系统中可以用
- stack和heap
- RAM中的两块内存区域
- 在stack中执行分配和赋值操作特别简单,asm汇编语句只有一句话,而在heap上分配则非常麻烦,所以在stack的内存足够情况下,请优先使用stack的内存。
- 宏
- 单纯的文字替换
- 用来ifdef else,宏替换,在debug和release状态切换
- auto
- 简化代码,在迭代的时候用比较好
using DeviceMap = std::unordered_map<std::string, std::vector<Device*>>; typedef std::unordered_map<std::string, std::vector<Device*>> DeviceMap; auto DeviceMap = Get();
- 简化代码,在迭代的时候用比较好
- 函数指针
- 不是关键字的可以作为一个函数指针
void PrintValue(int value) { cout << "value:" << value << endl; } void ForEach(const vector<int>& values, void(*func)(int)) { for (int value : values) func(value); } int main() { vector<int> values = { 1,5,3,2,4 }; ForEach(values, PrintValue); <!-- lambda --> ForEach(values, [](int value) {cout << "value" << value << endl; }); std::cin.get(); }
- 不是关键字的可以作为一个函数指针
- lambda表达式可以用来表示函数指针
- []里面表示如何传入参数,[=]表示传输值,[\&]表示传入地址,或者[&a,b]表示a传地址,b传值
- using namespace std
- 为什么不用这个?清晰显示来源
- 什么是namespace,避免naming conflict
- thread
static bool s_Finished = false; void DoWork() { using namespace std::literals::chrono_literals; std::cout << "started thread id = " << std::this_thread::get_id() << std::endl; while (!s_Finished) { std::cout << " working" << std::endl; std::this_thread::sleep_for(1s); } } int main() { std::thread worker(DoWork); //worker.join(); // join=等待worker完成 std::cin.get(); s_Finished = true; worker.join(); std::cout << "finished" << std::endl; std::cin.get(); }
- Timing
- 巧用time计时
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 << "Time took " << ms << " ms " << std::endl; } }; void Function(){ Timer timer; for (int i = 0; i < 100; i++) std::cout << "hello " << std::endl; } int main() { Function(); std::cin.get(); }
- 巧用time计时
- 多维数组
- int**(指向int *的指针)
- sort
- 注意包含的头文件
```C++
#include
#include #include
int main() { std::vector
values = { 3,5,1,2,4 }; std::sort(values.begin(), values.end(), [](int a, int b){ return a > b; }); std::sort(values.begin(), values.end(), std::greater ()); for (int value : values) std::cout << value << std::endl; std::cin.get(); } ``` - 注意包含的头文件
```C++
#include
- 类型双关type punning
- C++可以直接得到内存,
struct Entity { int x, y; }; int main() { Entity entity = { 1,3 }; int *position = (int*)&entity; //position 是一个 int 的数组,将地址解析为另一种类型 int y = *(int*)((char*)&entity + 4); std::cout << position[0] << "," << position[1] << std::endl; //1,3 std::cin.get(); }
- C++可以直接得到内存,
- union
- 共享内存
struct Vector2 { float x, y; }; struct Vector4 { union { struct { float x, y, z, w; }; struct { Vector2 a, b; }; }; }; void PrintVector2(const Vector2& vector) { std::cout << vector.x << ", " << vector.y << std::endl; } int main() { Vector4 vector = { 1.0f, 2.0f, 3.0f,4.0f }; PrintVector2(vector.a); PrintVector2(vector.b); vector.z = 500.0f; PrintVector2(vector.a); PrintVector2(vector.b); std::cin.get(); }
- 共享内存
- virtual destructions
- 如果没有这个,只是单纯实现两个析构函数,则可能导致内存泄漏 ```C++ class Base { public: Base() { std::cout « “Base Constructor\n”; } ~Base() { std::cout « “Base Destructor\n”; } };
class Derived :public Base { public : Derived() { m_Array = new int[5]; std::cout « “Derived Constructor\n”; } ~Derived() { delete[] m_Array; std::cout « “Derived Constructor\n”; } private: int* m_Array; }; int main() { Base* base = new Base(); delete base; std::cout « ”————-\n”; Derived* derived = new Derived(); delete derived; std::cout « ”————-\n”; Base* poly = new Derived(); delete poly; std::cout « ”————-\n”; std::cin.get(); } Base Constructor Base Destructor ————- Base Constructor Derived Constructor Derived Constructor Base Destructor ————- Base Constructor Derived Constructor Base Destructor 只销毁base,出现内存泄露 ————- virtual ~Base() { std::cout « “Base Destructor\n”; } // 这样就好 Base Constructor Derived Constructor Derived Constructor Base Destructor ———— ```
- casting
- 几种
- 调试
- action可以在console中显示当前的变量的值。不需要断点查看。
- condition可以在一定条件下出发断点。
- safety
- 智能指针
- 预编译
- 节省时间,需要设置。emmm,没记下来
- dynamic casting
-
benchmarking
- visual studio 转到反汇编, ALT+G,确认需要编译的没有被优化
- project->c++->language->language standard
std::optional<std::string> ReadFileAsString(const std::string& filepath) { std::ifstream stream(filepath); if (stream) { std::string result; stream.close(); return result; } return {}; } int main() { std::optional<std::string> data = ReadFileAsString("data.txt"); if (data.has_value()) { std::cout << "File read successfully"; } else { std::cout << "File can not be opened!!"; } std::cout << "adf" << std::endl; }
- 使用any类型
- 少用
- 加速
- 使用并行的for loop。
- mutex,互斥锁,lock该资源
static std::mutex s_Mutex; static void Load(vector<Ref<Mesh>> & meshes, string filepath) { auto mesh = Mesh::load(filepath); lock_guard<std::mutex> lock(s_Mutex); meshes.push_back(mesh); }
- 字符串优化
- 使用c_str,使用const char *,使用string_view
static uint32_t s_AllocCount = 0; void* operator new(size_t size) { s_AllocCount++; std::cout << "Allocating " << size << " bytes \n"; return malloc(size); } void PrintName(std::string_view name) { //void PrintName(const std::string& name) { std::cout << name << std::endl; } int main() { std::string name("Wangyuqin Shuicahoyang"); // string的构造函数会先分配8个字节,如果右值大于8字节,则会allocate。 std::string filename = name.substr(0, 4); std::string lastname = name.substr(5, 10); std::string_view firstName(name.c_str(), 3); std::string_view lastName(name.c_str() + 4, 4); const char* name = "Wangyuqin Shuichaoyang"; std::string_view firstName(name, 3); std::string_view lastName(name + 4, 4); PrintName("Wang Yuqin"); std::cout << s_AllocCount << " allocation" << std::endl; std::cin.get(); }
- 使用c_str,使用const char *,使用string_view
- singleton
- 随机数产生的class适合用单例模式 ```C++ class Random { public: Random(const Random&) = delete; static Random& Get() { return s_Instance; } float Float() { return m_Random; } private: Random() {} float m_Random = 0.5f; static Random s_Instance; };
Random Random::s_Instance; int main() { float number = Random::Get().Float(); std::cout « number « std::endl; std::cin.get(); } ```
- 左值和右值
int& GetValue() { static int value = 10; return value; } void SetValue(const int & i) { return ; } int main() { const int i = GetValue(); // 左值一定有内存,non const的 GetValue() = 5; SetValue(19); SetValue(i); //一个const的左值可以接受const和non const的右值。 std::cin.get(); }
- 静态检查工具
- PVS-Studio
- C++参数计算顺序
- 没有固定顺序,C++17令顺序是一个接着一个,不能是并行输入的,但是从左至右计算还是从右至左计算是不确定的。
void PrintSum(int a, int b) { std::cout << a << " + " << b << " = " << (a + b) << std::endl; } int main() { int value = 0; PrintSum(value++, value++); std::cin.get(); }
- 没有固定顺序,C++17令顺序是一个接着一个,不能是并行输入的,但是从左至右计算还是从右至左计算是不确定的。