@[TOC]
指针Pointers 指针是一个整数,储存一个内存地址。
类型没有意义,只是在这个地址的数据是这个类型
1 2 3 4 5 6 7 8 9 int var = 8 ;void * ptr = &var;*ptr = 10 ; ========================= char * buffer = new char [8 ]; memset (buffer, 0 , 8 );delete [] buffer;buffer = nullptr ;
指针本身也只是变量,这些变量也储存在内存中(double pointers,triple pointers-指针的指针)
1 2 3 char * buffer = new char [8 ]; memset (buffer, 0 , 8 );char ** ptr = &buffer;
引用Reference 指针和引用基本是一回事,引用是对某个存在变量的引用。引用本身不是一个新的变量,并不真正占用内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int a = 5 ;int * b = &a;int & ref = a;void increment (int value) { value++; } increment (a);void increment (int * value) { (*value)++; } increment (&a) void increment (int & value) { value++; } increment (a)
引用只是让代码更好看了,没有什么事情是引用能做但指针不能的
一旦声明一个引用,就不能更改它所引用的对象
类Class C++支持面向过程,基于对象,面向对象,泛型编程
类是一种将数据和函数组织在一起的方式
由类类型制成的变量叫对象,新创建对象的过程叫做实例化
默认情况下,类成员的访问控制都是私有的,只有类内部的函数才能访问——提供公有函数访问接口
类内的函数称为方法(method)
与结构体的区别 默认情况下,类是私有的,结构体是私有的
一般不对结构体使用继承,或过于复杂的功能,仅仅保持结构体为数据和简单的方法
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 class Log { public : const int LogLevelError = 0 ; const int LogLevelWarning = 1 ; const int LogLevelInfo = 2 ; private : int m_LogLevel = LogLevelInfo; public : void SetLevel (int level) { m_LogLevel = level; } void Error (const char * message) { if (m_LogLevel >= LogLevelError) std::cout << "[ERROR]: " << message << std::endl; } void Warn (const char * message) { if (m_LogLevel >= LogLevelWarning) std::cout << "[WARNING]: " << message << std::endl; } void Info (const char * message) { if (m_LogLevel >= LogLevelInfo) std::cout << "[Info]: " << message << std::endl; } } int main () { Log log; log.SetLevel (log.LogLevelWarning); Log.Error ("Hello!" ); Log.warn ("Hello!" ); Log.Info ("Hello!" ); std::cin,get (); }
Static静态关键字 类外或结构体外的static修饰符在link阶段是局部的,它只对定义它的编译单元(.obj)可见
类内或结构体里面的static表示这部分内存是这个类的所有实例共享的,即就算实例化了很多次这个类或结构体,这个静态static变量只会有一个实例,并被共享。
静态方法也是如此,没有该实例的指针(this)
类外的static。 static.cpp:
1 2 static int s_Variable = 5 ;
main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <iostream> int s_Variable = 10 ;int main () { std::cout << s_Variable << std::endl; std::cin.get (); } ========================= #include <iostream> extern int s_Variable;int main () { std::cout << s_Variable << std::endl; std::cin.get (); }
类内Static 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 #include <iostream> struct Entity { int x,y; void Print () { std::cout << x << ", " << y <<std:endl; } } int main () { Entity e; e,x = 2 ; e.y = 3 ; Entity e1 = { 5 , 8 }; e.Print (); e1. Print (); std::cin.get (); } ============================================ #include <iostream> struct Entity{ static int x,y; void Print () { std::cout << x << ", " << y <<std:endl; } } int Entity::x;int Entity::y;int main () { Entity e; e.x = 2 ; e.y = 3 ; Entity e1 e1. x = 5 ; e1. y = 8 ; e.Print (); e1. Print (); std::cin.get (); }
局部静态Local Static 变量的
生命周期:变量被删除前在内存中停留多久
作用域:在哪里能访问这个变量
一个静态局部变量允许我们定义一个变量,生命周期是整个程序,但作用于被限制在这个函数里或任何作用域。和类内作用域的静态变量是一样的。
1 2 3 4 5 6 7 #include <iostream> void Function () { static int i = 0 ; i++; std::cout << i << std::endl; }
ENUMS枚举 1 2 3 4 5 6 7 8 9 10 11 12 enum Example { A,B,C }; int main () { Example value = B } ============ enum Example : unsigned char { }
1 2 3 4 5 6 7 8 9 class Log {public : enum Level { Error, Warning, Info }; }
Constructor构造函数 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 class Entity {public : float X, Y; void Init () { X = 0.0f ; Y = 0.0f ; } void Print () { std::cout<< X<< ", " << Y << std::endl; } } int main () { Entity e; e.Init (); e.Print (); } ================================================= class Entity{public : float X, Y; Entity (){ X = 0.0f ; Y = 0.0f ; } Entity (float x, float y){ X = x; Y = y; } } int main () { Entity e; Entity e2 (10.0f , 5.0f ) ; e.Print (); e2. Print () }
当使用static声明类中元素时,并不会调用构造函数(因为static是所有实例共有的)
特殊类型的构造函数:拷贝构造函数Copy Constructor, Move Constructor
Destructors析构函数 constructor 在创建一个对象实例时运行,而析构函数在摧毁一个对象时运行
构造函数通常用来设置变量或者进行某些需要的初始化
当对象即将结束生命周期时,需要清理原本占用的内存,
析构函数同时使用堆和栈来给对象分配空间,所以如果使用new给对象分配空间,而对应的需要使用delete调用析构函数释放空间
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 class Entity {public : float X, Y; Entity (){ std::cout << "Created Entity!" << std::endl; X = 0.0f ; Y = 0.0f ; } Entity (float x, float y){ X = x; Y = y; } ~Entity (){ std::cout << "Destroyed Entity!" << std::endl; } } void Function () { Entity e; e.Print (); } int main () { Function (); std::cin.get (); }
Inheritance继承 (Code Duplication)
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 class Entity {public : float X,Y; void Move (float xa, float ya) { X += xa; Y += ya; } }; class Player : public Entity{public : const char * Name; void PrintName () { std::cout<< Name <<std::endl; } }; int main () { Player palyer; player.PrintName (); palyer.Move (5 , 10 ); }
Virtual Functions虚函数 虚函数允许我们覆盖子类中的方法
比如B是A的派生类(子类),如果指定A中某虚函数方法,则可以在B中覆盖该方法做别的事
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 Entity {public : std::string GetName () { return "Entity" ; } }; class Player : public Entity {private : std::string m_Name; public : Player (const std::string& name) : m_Name (name) {} std::string Getname () { retrun m_Name; } }; void PrintName (Entity* entity) { std::cout << entity->GetName () << std::endl; } int main () { Entity* e = new Entity (); PrintName (e) Player* p = new Player ("Cherno" ); std::cout << p->GetName () << std::endl; PrintName (p) }
虚函数引入了动态分派Dynamic Dispatch,通常使用VTable虚函数表实现此编译
虚函数表包含基类中所有虚函数的映射,在运行时映射向正确的覆写函数
总之,如果要覆盖函数,需要在基类中将基函数标记为虚函数
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 class Entity {public : virtual std::string GetName () { return "Entity" ; } }; class Player : public Entity {private : std::string m_Name; public : Player (const std::string& name) : m_Name (name) {} std::string Getname () override { retrun m_Name; } }; void PrintName (Entity* entity) { std::cout << entity->GetName () << std::endl; } int main () { Entity* e = new Entity (); PrintName (e) Player* p = new Player ("Cherno" ); std::cout << p->GetName () << std::endl; PrintName (p) }
虚函数的性能代价,是需要遍历整个虚函数表查看映射
Pure virtual function纯虚函数 纯虚函数允许我们定义一个在基类中没有实现的函数,然后迫使在子类中实际实现
在接口中,类仅仅包含未实现的方法并充当一种勉强的模板,这种类无法实例化,必须在子类中实现
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 class Entity {public : virtual std::string GetName () = 0 ; }; class Player : public Entity {private : std::string m_Name; public : Player (const std::string& name) : m_Name (name) {} std::string Getname () override { retrun m_Name; } }; void PrintName (Entity* entity) { std::cout << entity->GetName () << std::endl; } int main () { Entity* e = new Player ("" ); PrintName (e); Player* p = new Player ("Cherno" ); std::cout << p->GetName () << std::endl; PrintName (p) }
只有当一个类提供所有纯虚函数的实现,才能实例化。
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 class Printable {public : virtual std::string GetClassName () = 0 ; }; class Entity : public Printable {public : virtual std::string GetName () { return "Entity" ; } std::string GetClassName override { return "Entity" ; } }; class Player : public Entity {private : std::string m_Name; public : Player (const std::string& name) : m_Name (name) {} std::string Getname () override { retrun m_Name; } std::string GetClassName override { return "Player" ; } }; void PrintName (Entity* entity) { std::cout << entity->GetName () << std::endl; } void Print (Printable* obj) { std::cout << obj->GetClassNmae <<std::endl; } int main () { Entity* e = new Entity (); Print (e) Player* p = new Player ("Cherno" ); Print (p) }
Visibility访问控制 本质指类中的成员数据及函数的可访问性
基本的访问控制修饰符:public公有, protected保护, private私有
1 2 3 4 5 6 7 8 9 10 class Entity { int X,Y; public : Entity () { X = 0 ; } }; class Player : public Entity { };
Array数组 1 2 3 4 5 6 7 int example[5 ]; example[0 ] = 0 ; std::cout << example[0 ] << std::endl; std::cout << example << std::endl; int * ptr = example;*(ptr+2 ) = 6 ;
new 运算符动态分配内存空间,
delete 运算符释放,否则即便局部作用域的程序运行完毕,内存也不会被回收。
为class分配内存空间时,还会同时运行构造函数。
实例的地址所指向的就是成员变量的地址;但如果用new关键字来分配,为成员变量建立了一个指针,实例的地址指向的是指针的地址。
Strings字符串 1 2 3 4 5 6 7 8 const char * name = "Cherno" ;const char name2[6 ] = {'C' ,'h' ,'e' ,'r' ,'n' ,'o' }std::cout<<name2<<std::endl; const char name2[7 ] = {'C' ,'h' ,'e' ,'r' ,'n' ,'o' ,0 }std::cout<<name2<<std::endl; name[2 ] = 'a' ;
standard string 1 2 3 4 5 6 7 8 9 10 11 #include <string> int main () { std::string name = "Cherno" ; name += "hello!" ; std::string name2 = std::string ("Cherno" ) + “hello!"; //这也是可以的 }
string literal 字符串是不可变的。默认为const,在内存中只读
1 2 3 4 const char * name = u8"Cherno" ;const wchar_t * name2 = L"Cherno" ;const char16_t * name3 = u"Cherno" ;const char32_t * name4 = U"Cherno" ;
在C++14中,还可以
1 2 3 4 5 6 7 8 9 10 using namespace std::string_literals;std::string name0 = "Cherno" s + "hello" ; std::wstring name0 = L"Cherno" s + L"hello" ; std::u32string name0 = U"Cherno" s + U"hello" ; const char * example = R"(Line1 Line2 Line3 Line4)"
Const常量 就像一个承诺,认为一个变量不改变,但是否信守承诺还是取决于你自己。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const int MAX_AGE = 90 const int a =5 ;int * a = new int ;const int * a = new int ;*a = 2 ; a = (int *)&MAX_AGE; ================================ int const * a = new int ;================================= int * const a = new int ;*a = 2 ; a = (int *)&MAX_AGE; ================================
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 class Entity {private : int * m_X,m_Y; mutable int var; public : int GetX () const { return m_X; } int GetX () { m_X = 0 return m_X; } void SetX (int x) { m_X = x; } const int * const GetX () const { retrun m_X; } }; void PrintEntity (const Entity& e) { std::cout<<e.GetX ()<<std::end;; }
Mutable关键字 与const共同使用
1 2 3 4 5 6 7 8 9 10 11 class Entity { private : std::string m_Name; mutable int m_DebugCount = 0 ; public : const std::string& GetName () const { m_DebugCount++; return m_Name; } };
lambda
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int x = 8 ;auto f = []() { std::cout<<"Hello" <<std::endl; } auto f = [&]() { std::cout<<x<<std::endl; } auto f = [=]() { x++; std::cout<<x<<std::endl; } auto f = [=]() mutable { x++; std::cout<<x<<std::endl; }; f ();
Member Initializer Lists成员初始化列表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Entity { private : std::string m_Name; int m_Score; public : Entity () { m_Name = "Unknown" ; } Entity () : m_Name ("Unknown" ),m_Score (0 ) { } Entity (const std::string& name) :m_Name (name) { } } int main () { Entiyt e0; }
此外,实际上在不同的构造函数中,如果对
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Entity { private : std::string m_Name; Example m_Example; public : Entity () { m_Name = "Unknown" ; } Entity () { m_Name = "Unknown" ; m_Example = Example (8 ) } Entity () : m_Name ("Unknown" ),m_Example (Example (8 )),m_Example (8 ) { } Entity (const std::string& name) :m_Name (name) { } }
Ternary Operators三元操作符 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 if (s_Level>5 ){ s_Speed = 10 ; } else { s_Speed = 5 ; } s_Speed = s_Level>5 ? 10 : 5 ; std::string rank = s_Level > 10 ?"Master" : "Beginner" ; std::string rank; if (s_Level>10 ) rank = "Master" ; else rank = "Beginner" ; s_Speed = s_Level > 5 ? s_Level > 10 ? 15 : 10 : 5 ; s_Speed = s_Level > 5 ? (s_Level > 10 ? 15 : 10 ) : 5 ; s_Speed = s_Level > 5 && s_Level < 100 ? s_Level > 10 ? 15 : 10 : 5 ;