The Cherno C++ 教程 笔记 原视频
b站搬运
仅仅自用知乎已经有很多优秀的笔记了
C++ 如何工作
建一个project 在 资源文件(source)添加cpp文件
源文件
#后面是预处理语句 # include <iostream> 编译器会把iostream 和我们的代码黏起来
<< 重载运算符
debug 会默认关掉所有优化 release模式会更快 我们可以定制编译的是exe
还dll(库文件)
cpp 编译(会把预处理的头文件和你的cpp粘好一起编译) object file.obj
linker(在属性里可以定义一些选项)合并多个obj(解析obj之间依赖的符号比如外部函数)为执行文件
ctrl +F7 单独编译当前文件所以不会有exe()
visual studio 存放很奇怪文件orz
我们查bug 主要看 output window error list是垃圾
申明 告知编译器函数存在
头文件可以用来放一堆申明函数
定义 定义一个函数要干什么
编译
在项目 属性 C/C++ 预处理器 ->预处理到文件设置 是 会得到
.j文件是预处理结束后的文件
#define INTEGER int C++会替换前面的词 为后面的词
obj 文件是 二进制 我们可以用 项目属性 c/c++ 输出文件 汇编程序输出里
设置输出可读汇编语言
result=a*b
return result
和之间return result 在debug
和release模式是不一样的你可以查看汇编代码发现这一点
链接 主要焦点是找到每个符号和函数在哪里
static int Multiply
static 意味着该函数只在这个cpp翻译单元里定义
1 2 3 4 inline void Log (const char * message) { std::cout<< message<< std::endl; }
inline 会把Log函数替换为Log定义的内容
link c++需要一个入口点(Entry Point在 linker里面 )
入口点不一定是main
C++变量
不同变量在c++中唯一的区别就是存储的大小
int 一般 4字节(依赖编译器)
unsigned int
char short int long long long
char a=65 char a='A'
其他类型赋值字符会变成数字 shot a='A' 打印为65
float variable= 5.5 //其实定义了双精度double
float variable= 5.5f//f可以大写也可以小写 表示定义的是浮点数
bool 1 byte 虽然理论是只要1bit
sizeof(variable) 返回变量大小
指针(pointer)* 和引用(reference)&
c++ 函数
为了减少代码重复
调用函数 会使用堆栈结构
每次我们调用函数时,编译器生成call指令 创建一个堆栈结构
把参数这样的东西推进堆栈 把一个放回地址压入堆栈
在跳到二进制执行文件的不同部分执行函数指令
为了push回结果还要回到调用函数的地方 在内存里跳来跳去
所以有时候要用inline
c++头文件
#pragma once //pragma 编译之前先处理 once 只在一个翻译单元复制一次
1 2 3 4 5 6 7 8 #ifndef _LOG_H #define _LOG_H void InitLog () ;strcut Player () ;#endif
引号可以指定相对路径的文件
尖括号只用与编译器包含的路径
visual studio 配置
在设置断点以后按F5 右键断点 go to disassembly 可以查看汇编
visual studio 显示的 解决方案目录不是真实的点击
显示所有文件可以看到真实的目录 可以建一个src 文件夹 把所有
cpp文件加入到里面
我们可以自定义visu studio 的输出文件和 中间文件目录
在项目的属性常规里
设置输出目录$(SolutionDir)bin\$(Platform)\$(Configuration)\
中间目录
$(SolutionDir)bin\intermediates\$(Platform)\$(Configuration)\
$(SolutionDir)
这些宏的意义可以在编辑里查看
c++指针
指针是一个整数 存储地址的数字
指针的类型的类型是告诉编译器存的地址指的东西是包含多少字节的
这样可以在用*
读取的时候配置适当内存
int*ptr
指针表示 当我们用*ptr= 10;
要设置四字节的内存所以void *ptr
指针 不能
*ptr=10
1 2 3 void * ptr = 0 ; int var = 8 ;void * ptr2 = &var;
1 2 3 4 5 char * buffer = new char [8 ];memset (buffer, 0 , 8 );char ** ptr = &buffer;delete [] buffer;
C++引用
引用本身不是变量
1 2 3 int & int a = 5 ;int & ref = a;
引用可以把变量传入函数 并改变它
指针也可以做到这一点但要注意改的是地址还是地址里的值(注意优先级
*value++
改的是地址 (*value)++
改的是值)
c++类
类只是对数据和功能组合在一起的一种方法。类不会带来新的功能
没有它也可以实现你想要到 它有点像语法糖 。
与结构体的对比
类一般默认是私有的 结构体默认是公开的
可以用#define struct class
把struct 定义为 class 当public
和private要重写
static 有不同意义 取决上下文
一种在类和结构体外表示该变量只在他定义的翻译单元生效
另一种在类和结构体内 表示这个类和结构体的静态变量和方法 在所有实例里生效
改变一个所有实例都会改变 可以直接用类名加变量名或者函数名引用
和命名空间很类似比如 std::cout
同时在函数中使用static 可以延长变量的存在的周期 如果没有static
函数内变量会在 函数使用完后销毁 有了static 这个变量会在函数用完后还存在
且只能在函数内使用 不能再外部调用
extern var尝试从其他翻译单元找到变量var的定义
尽量用静态变量
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 #include <iostream> void Log (const char * message) ;class Player { public : int x, y; int speed; void Move (int xa, int ya) { x += xa * speed; y += ya * speed; } void Print () { std::cout << x << ", " << y << std::endl; } }; class Singleton { private : static Singleton* s_Instance; public : static Singleton& Get () { return *s_Instance; } void Hello () { Log ("mmm" ); } }; class Singleton2 { public : static Singleton2& Get () { static Singleton2 instance; return instance; } void Hello () { Log ("mmmm" ); } }; Singleton* Singleton::s_Instance = nullptr ; int main () { Singleton::Get ().Hello (); Singleton2::Get ().Hello (); if (6 == 6 ) Log ("Hello World!" ); void * ptr = 0 ; int var = 8 ; void * ptr2 = &var; Player player; player.x = 5 ; player.y = 5 ; Player player2 = { 8 ,7 }; player2.Print (); for (int i = 0 ; i < 5 ; i++) { if (i % 2 == 0 ) continue ; Log ("Hello" ); char * buffer = new char [8 ]; memset (buffer, 0 , 8 ); char ** ptr = &buffer; delete [] buffer; std::cout << i << std::endl; } std::cin.get (); }
c++ 枚举
1 2 3 4 5 enum MyEnum { A,B,C }; MyEnum test = A;
c++虚函数
可以复写继承类的虚函数实现特定功能
会生成v表来实现引入额外内存开销但是是可以接受的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Play { public : virtual std::string GetName () { return "Play" ; } }; class Player : public Play{ private : std::string m_Name; public : Player (const std::string& name) : m_Name (name) {} std::string GetName () override { return m_Name; } }; void PrintName (Play* e) { std::cout << e->GetName () << std::endl; }
纯虚函数允许我们定义一个没有定义的函数然后强迫使用子类去实现它
不然不能实例化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Printable { public : virtual std::string GetClassName () = 0 ; }; class classA : public Printable{ std::string GetClassName () override { return "A" ; } }; void Printclass (Printable* obj) { std::cout << obj->GetClassName () << std::endl; }
类中的可见性
private 只能在类中或者类的友元调用访问 protected
可以在子类和定义类调用但是不能在类外面用 public哪都行
1 2 3 int expmle[5 ];int * ptr = example;*(int *)((char *)ptr + 8 ) = 6 ;
1 2 int * another = new int [5 ]; delete [] another;
1 int * another = new int [5 ];
1 2 3 4 #include <array> int a[5 ];int count = sizeof (a)/sizeof (int );std::array<int ,5> another;
const
1 2 const char * name="mkk" ;char name2[4 ]={'m' ,'k' ,'k' ,0 };
1 2 3 4 5 6 7 #include <string> #include < stdlib.h> std::string name="mkk" ; name += " hello!" ; bool contains = name.find ("kk" ) != std::string:npos
1 2 3 4 void PrintString (const std::string& string) { std::cout << string << std::endl; }
1 2 3 4 const char * name = u8"mkk" ;const wchar_t * name2 = L"mkk" ;const char16_t * name3 = u"mkk" ;const char32_t * name4 = U"mkk" ;
1 2 3 4 5 6 7 8 9 10 const int MAX_AGE = 90 ;const int * a = new int ;*a = 2 ; a = (int *)&MAX_AGE; int * const b = new int ;*b = 2 ; b = (int *)&MAX_AGE; const int * const c = new int ;*c = 2 ; c = (int *)&MAX_AGE;
1 2 3 4 5 6 7 8 9 10 11 12 13 class Entity { private : int m_X, m_Y; mutable int var; public : int GetX () const { var=2 ; return m_X; } }
1 2 3 4 5 6 7 8 9 10 11 class Entity { private : int * m_X, m_Y; public : const int * const GetX () const { return m_X; } }
1 2 3 4 void PrintEntity (const Entity& e) { std::cout << e.GetX () << std::endl; }
1 2 3 4 5 6 7 8 9 10 11 int x = 8 ;auto f = [=]() mutable { x++; std::cout << x << std::endl; }; f ();std::cout << x << std::endl;
三元判断算符
python
c++
julia
new
在堆上创建 会比较慢 要主动delete 不然会一直在
1 2 3 4 5 6 7 8 9 int a = 2 ;int * b = new int [50 ];Entity* e = new Entity (); delete [] b;delete e;
隐式转换
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 Entity { private : std::string m_Name; int m_Age; public : Entity (const std::string& name) : m_Name (name), m_Age (-1 ){} Entity (int age) : m_Name ("Unknown" ) , m_Age (age) {} }; void PrintEntity (const Entity& entity) { } int main{ PrintEntity (22 );PrintEntity (Entity ("Cherno" ));Entity aa = Entity ("mkk" ); Entity bb = 22 ; Entity* aaa = new Entity (std::string ("mkk" )); delete aaa;}
运算符重载
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 33 34 35 36 37 38 struct Vector2 { float x, y; Vector2 (float x, float ) : x (x) ,y (y) {} Vector2 Add (const Vector2& other) const { return Vector2 (x + other.x, y + other.y); } Vector2 operator +(const Vector2& other) { return Add (other); } bool operator ==(const Vector2& other) const { return x == other.x && y == other.y; } }; std::ostream& operator <<(std::ostream& stream, const Vector2& other) { stream << other.x << "," << other.y ; return stream; } int main () { Vector2 p (4.0f , 4.0f ) ; Vector2 s (0.5f , 1.1f ) ; Vector2 result1 = p + s; std::cout << p << std::endl; std::cout << result1 << std::endl; std::cin.get (); }
this
this 是一个指向当前对象实例的指针,该方法属于这个对象的实例。
为了调用非静态方法,要一个实例化对象 这就要用到this
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 Void PrintEntity (const Entity& e) class Entity { public : int x,y; Entity (int x ,int y) { this ->x=x; this ->y=y; Entity& e = *this ; PrintEntity (*this ); } int GetX () const { const Entity& e = *this ; } } Void PrintEntity (const Entity& e) { }
作用域指针
自动消除new 创建的东西
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class ScopedPtr { private : Entity* m_Ptr; public : ScopedPtr (Entity* ptr) : m_Ptr (ptr) { } ~ScopedPtr () { delete m_Ptr; } }; int main () { { ScopedPtr e = new Entity (); } }
智能指针
让你摆脱new 和delete的方法
unique_ptr你不能复制他 一复制就消失一个
shared_ptr会计数你 的引用(复制或者共享) 当计数为0 释放内存
weak_ptr 不会增加引用计数其他和shared_ptr一样
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 { std::unique_ptr<Entity> entity = std::make_unique <Entity>(); entity->Print (); } { std::shared_ptr<Entity> e0; { std::shared_ptr<Entity> sharedEntity = std::make_shared <Entity>(); std::shared_ptr<Entity> e0 = sharedEntity; } } { std::weak_ptr<Entity> e1; { std::shared_ptr<Entity> sharedEntity1 = std::make_shared <Entity>(); e1 = sharedEntity1; } }
c++的复制与拷贝
1 2 3 Vector2 * a= new Vector2(); Vector2 * b =a; b->x=2;
箭头算符->
1 2 3 4 5 6 7 8 9 10 struct Vector3 { float x, y,z; }; int main () { int offset= (int )&((Vector3*)nullptr )->z; std::cout<< offset << std:;endl; std::cin.get (); }
动态数组
Vector 其实是arraylist
一般很慢要优化
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <iostream> #include < string> #include <vector> struct Vertex { float x, y, z; Vertex (float x, float y, float z) : x (x), y (y), z (z) { } Vertex (const Vertex& vertex) : x (vertex.x) ,y (vertex.y),z (vertex.z) { std::cout << "copied!" << std::endl; } }; std::ostream& operator <<(std::ostream& stream, const Vertex& vertex) { stream << vertex.x << "," << vertex.y << ',' << vertex.z; return stream; } void Function (const std::vector<Vertex>& vertices) {} int main () { std::vector<Vertex> vertices; vertices.push_back ({ 1 ,2 ,3 }); vertices.push_back ({16 ,73 ,9 }); Function (vertices); for (int i = 0 ; i < vertices.size (); i++) std::cout << vertices[i] << std::endl; vertices.erase (vertices.begin () + 1 ); for (Vertex v : vertices) std::cout << v << std::endl; std::cin.get (); }
优化技巧
vertices.reserve(3);
vertices.emplace_back(1,2,3);
c++使用库
库包含 库目录library和包含目录include 包含目录是一堆头文件
lib目录是预先构建的二进制文件
动态库和静态库区别是静态库已经包含在exe里面了
而动态库要是通过exe在运行时调用外部库函数要额外链接
https://www.glfw.org/
下你要的文件 32 或64
然后会用到include 和lib-vrc20??(选最新的)
在lib-vrc20??里面
glfw3.dll 运行时动态链接的库
glfw3dll.lib是一种静态库 和glfw3.dll一起用
(可以不用我们可以在glfw3.dll访问 但有它可以更快)
glfw3.lib静态库(你不想用其他两个dll可以链接它)
C++创建使用库
创建一个game项目 在里面创建src 创建Appliction.cpp 在game
添加一个Engine项目 然后设置属性 常规 配置类型为静态库 添加 Engine
一个src 创建Engine.h Engine.cpp
点击game解决方案属性 C/C++ 附属包含目录填入
$(SolutionDir)Engine\src;
点击game解决方案在添加里点引用勾选 Engine
C++返回多个值方法
写一个结构体返回(可以返回不同类型)
传入引用
传入指针 (可以传入nullptr 做一个检测决定是否改变值)
返回一个数组
Array会在栈上创建 Vector 会把它的底层存在堆上 所以
std::array会更快
返回元组tuple or pair
c++模板template
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 <iostream> #include <string> template <typename T>void Print (T value) :{ std::cout << value << std::endl; } template <typename T ,int N>class Array { private : T int m_Array[N]; public : int GetSize () const { return N;} }; int main { Print <int >(5 ); Print (5.5f ); Array<int ,5 > array; std::cout << array.GetSize () << std::endl; std::cin.get (); }
mete programme
C++的堆与栈内存的比较
C++宏
这里主要讲在c++用预处理器来“宏”化一些操作。
1 2 3 4 5 6 7 8 9 10 #include <iostream> #include <string> #define std::cin.get() #define LOG(x) std::cout << x << std::endl int main { LOG ("hello" ); WAIT; }
在visual studio 里面Debug 模式定义 C/C++ preprocessor 里面
定义PR_DEBUG
Release 模式定义 C/C++ preprocessor 里面
定义PR_RELEASE可以利用下面的代码在不同模式选择是否打印
1 2 3 4 5 6 7 #ifdef PR_DEBUG #define LOG(x) std::cout << x << std::endl #esle #define LOG(X) #endif
\
enter 的转义你可能在写很长的宏用上 后面别接回车
把new 用宏 定义为一个别的词 可以自动跟踪 哪些文件用了它
分配了多少内存
auto
可以自动计算出类型
我们是否应该到处使用auto?
什么时候该用 当你会改变函数类型时 你赋值 的变量是auto
你就不用改变它代码
auto 会使你搞不清楚变量类型 可读性差 特别是兼容性类型
你会不知道是char* 还是 std:string
在使用template 时 你不得不用模板因为你不知道类型
用auto替换超长的类型
1 2 3 4 5 6 7 8 std::vector<std::string> string; strings.push_back ("Apple" ); string.push_back ("orange" ); for (auto it = string.begin (); it != strings.end () ; it++){ std::cout << *it << std::endl; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Device {};class DeviceManager { private : std::unordered_map<std::string,std::vector<Device*>> m_Devices; public : const std::unordered_map<std::string,std::vector<Device*>>& GetDevices () const { return m_Devices; } }; int main () { DeviceManager dm; const auto & devices = dm.GetDevices (); }
c++的静态数组
std::array有边界检查(Debug时有) 普通数组没有
1 2 3 4 5 6 7 8 9 10 #include <iostream> #include <array> int main () { std::array<int ,5> data; data[0 ] = 2 ; data[4 ] = 1 ; }
C++的函数指针
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 #include <iostream> #include <vector> void Helloworld (int a ) { std::cout << 'mkk' <<a<< std::endl; } void ForEach (const std::vector<int >& values,void (*func)(int )) { for (int value: values) func (value); } int main () { void (*mkk)(a) = Helloworld; mkk (1 ); typedef void (*Helloworldf) (int ) ; Helloworldf f1= Helloworld; auto function = Helloworld; function (1 ); f1 (1 ); std::vector<int > values={ 1 ,5 ,6 ,7 }; ForEach (values, [](int a){ std::cout << "value : " << a << std::endl;}); std::cin.get (); }
lambda
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <iostream> #include <vector> #include <functional> #include <algorithm> void ForEach (const std::vector<int >& values,const std::function<void (int )>& func) { for (int value: values) func (value); } int main () { std::vector<int > values={ 1 ,5 ,6 ,7 }; auto it = std::find_if (values.begin (),values.end (),[](int value){ return value > 3 ; }); std::cout << *it std::endl; int a = 5 ; auto lambda = [=](int value) mutable { a=111 ;std:: cout << "Value:" <<a << std::endl;}; std::cin.get (); }
为什么不用 using namespace?
会很难分辨哪些是标准库的函数 同时外部的库使用 之间有同名函数也会混淆
甚至会出现没有编译错误找不到的bug
例子
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 #include <iostream> #inclued<string> namespece apple { void print (const std ::string& text) { std::cout << text << std::endl; } } namespece orange { void print (const char * text) { std::string temp =text; std::reverse (temp.begin (),temp.end) std::cout << temp << std::endl; } } int main{ using namespace apple; using namespace orange; print ("Hello" ); }
c++的名称空间
感觉没什么用 不过可以嵌套好像有点不同 但是嵌套会不会太复杂了
1 2 3 namespace a=apple;using namespace apple;using apple::print;
c++线程
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 33 34 35 36 #include <iostream> #include <thread> static bool s_Finished = false ;void Doworker () { using namespace std::literals::chrono_literals; std::cout << "started thread id= " << std::this_thread::get_id () << std::endl; while (!s_Finished) { std::cout << "working ..\n" ; std::this_thread::sleep_for (1 s); } } int main () { std::thread worker (Doworker) ; std::cin.get (); s_Finished = true ; worker.join (); std::cout << "finshed" << std::endl; std::cout << "started thread id= " << std::this_thread::get_id () << std::endl; std::cin.get (); }
c++时间
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 33 34 35 36 37 38 #include <iostream> #include <chrono> 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; } }; void Function () { Timer timer; for (int i = 0 ; i < 100 ; i++) std::cout << "Hello\n" ; } int main () { Function (); std::cin.get (); }
在你计时的时候要了解代码是否被计算 比如下面的代码 在debug模式时
编译的结果程序是会一步一步循环然后输出value 但是release 模式 会优化代码
编译时就已经计算完运算的值了 直接在编译结果的程序输出value。
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 33 34 35 36 37 38 39 class Timer { Timer () { auto m_StartTimerpoint = std::chrono::high_resolution_clock::now (); } ~Timer () { stop (); } void stop () { auto endTimepoint = std::chrono::high_resolution_clock::now (); auto start= std::chrono::time_point_cast <std::chrono::microseconds>(m_StartTimerpoint).time_since_epoch ().count (); auto end= std::chrono::time_point_cast <std::chrono::microseconds>(endTimepoint).time_since_epoch ().count (); auto duration = end -start; double ms = duration * 0.001 ; std::cout << duration<< "us (" <<ms<<"ms)\n" ; } private : std:;chrono::time_point<std::chrono::high_resolution_clock> m_StartTimerpoint; }; int main () { int value = 0 ; { Timer timer; for (int i = 0 ;i < 10000000 ; i++) value +=2 ; } std::cout<< value <<std::endl; __debugbreak(); }
C++多维数组
c++多维数组是数组的数组 即存一堆指针的指针。和我想的不一样
我以为是一个指针一堆连续内存有函数把一维的映射为多维。
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 int ** a2d = new int * [50 ];for (int i = 0 ; i < 50 ; i++) a2d[i] = new int [50 ]; int *** a3d = new int ** [50 ];for (int i = 0 ; i < 50 ; i++){ a3d[i] = new int * [50 ]; for (int j = 0 ; j < 50 ; j++) { a3d[i][j] = new int [50 ]; } } a3d[0 ][0 ][0 ] = 0 ; for (int i = 0 ; i < 50 ; i++) delete [] a2d[i]; delete [] a2d;for (int i = 0 ; i < 50 ; i++){ for (int j = 0 ; j < 50 ; j++) { delete [] a3d[i][j]; } delete [] a3d[i]; } delete [] a3d;
这样很慢 因为配置的内存不是连续的所以 会造成更多cache miss
c++排序
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 #include <iostream> #include <vector> #include <algorithm> int main () { std::vector<int > values = { 3 ,5 ,1 ,4 ,20 }; std::sort (values.begin (), values.end (), [](int a, int b) { if (a == 1 ) return false ; if (b == 1 ) return true ; return a< b; }); for (int value : values) std::cout << value << std:: endl; std::cin.get (); }
c++类型双关
把一段类型内存当另一个类型用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 struct Entity { int x, int y; }; int main () { int a = 50 ; double & value = *(double )&a; Entity e = {5 , 8 }; int * position = (int *)&e; int y = *((char *)&e +4 ); std::cout << position[0 ] << "," << position[1 ] <<std::endl; std::cout << y <<std::endl; std::cin.get (); }
c++union
c++虚构析函数
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 #include <iostream> 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 Destructor\n" ;} private : int * m_array; }; int main () { Base* base = new Base (); delet baes; std::cout<<"--------------\n" ; Derived* derived = new Derived (); delete derived; std::cout<<"--------------\n" ; Base* poly = new Derived (); delete poly; }
c++类型转换
c语言风格类型转换
1 2 3 double value = 5.25 ;double a = (int )value + 5.30 ;
c++风格类型转换
1 double s = static_cast <int >(value) +5.3 ;
和c功能没差别 但方便检索查看避错
static_cast reinterpret_cast(与类型双关有关) const_cast
dynamic_cast
使用时会存储 运行时类型信息(runtime typer information)RTTI
所以会有存储 RTTI 和读取RTTI的开销
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Entity { public : virtual void PrintName () {} }; class Player : public Entity{ }; class Enemy : public Entity{ }; int main () { Player* player = new Player (); Entity* actuallyPlayer = player; Entity* actullayEnemy = new Enemy (); Player* p0= dynamic_cast <Player*>(actullayEnemy); if (p0){}; Player* p1= dynamic_cast <Player*>(actullayPlayer); }
预编译头文件
预先编译不会改动头文件为二进制可以减少以后你改动项目代码 编译时间
visual studio
创建一个pch.h 内容你要预编译的头文件
pch.cpp内容#include "pch.h"
设置 属性
>c/c++>预编译头 >预编译头 选创建 以及 预编译头文件写pch.h
Main.cpp要有#include "pch.h"
项目属性 >c/c++>预编译头 >预编译头 选使用 以及
预编译头文件写pch.h
1 2 time g++ -std=c++11 pch.h time g++ -std=c++11 Main.cpp
OpenGL
一种跨平台的图形API 允许我们控制显卡 它的核心是一种规范
其他
一个c++参考网站 cppreference.com
Visual
Studio如何设置才能支持C++11/14/17/20这些新标准的特性?
这里以最新的 Visual Studio 2022 来说明一下设置方法吧,毕竟 Community
版本都是可以免费使用的。
当新建一个C++项目后(比如新建了一个“C++控制台应用”项目),然后在“解决方案资源管理器”中右击项目,在下拉菜单中点击最下面的“属性”。在打开的“属性”窗口中,选择“配置属性
| C/C++ | 语言”,找到“C++ 语言标准”。可以看到它的默认选择是支持“ISO
C++14标准”(如下图),当然C++14标准包含了C++11的特性
我们点开这个“C++ 语言标准”的下拉框,可以看到其它选择有“ISO
C++14标准”“ISO C++17标准”“ISO
C++20标准”等,按照需要选择其中一个就行了(提示:高版本的标准是包含低版本标准的特性的)。
如果选择了“ISO
C++20标准”,还需要注意下面的问题 :
Visual C++ 2022 对
C++20标准中的“模块”新特性尚未完全支持。编写或使用自己的“模块”通常没有问题,但是,导入标准库头文件(如下语句)还不能使用,编译会出错。
解决这个问题的方法,可以向项目中添加一个单独的头文件(比如叫“HeaderUnits.h”,如下图),
在这个头文件中预先导入本项目中需要导入的所有标准库头文件,比如
HeaderUnits.h 文件内容如下:
1 2 3 4 5 #pragma once import <iostream>;import <vector>;import <optional>;import <utility>;
接下来,在“解决方案资源管理器”中右击
HeaderUnits.h,选择下拉菜单的“属性”。然后在“属性”窗口中选择“配置属性 |
常规”,设置“项类型”为“C/C++编译器”(如下图),然后点击“应用”按钮。
下一步,选择“配置属性 | C/C++ | 高级”,将“编译为”设置为“作为 C++
标头单元编译”(如下图),然后点击“确定”按钮。(这一步不知道怎一设置就报错)
源地址
visual
Studio之如何快速的进行注释
注释:选定要注释的区域:ctrl+K,然后再ctrl+C。
解注释:选定要注释的区域:ctrl+K,然后再ctrl+U。