<> 与 “”引用的区别
“”
<>
单例类
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
| class Singleton { private: static Singleton* s_Instance; public: static Singleton& Get(){ return *s_Instance;} void hello(){}; }; Singleton* Singleton::s_Instance = nullptr; int main(){
Singleton::Get().hello(); };
int main(){
Singleton::Get().hello(); }
|
构造函数
比如我们有一个类——Entity,如果不手动定义一个构造函数,那么编译器会默认为我们构造一个空的构造函数
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Entity { public: float x, y; Entity(float x, float y){ X = x; Y = y; } void print(){ std::cout << X << "," << Y << std::endl; } };
int main(){ Entity e(10.0f, 5.0f); e.Print(); std::cin.get(); }
|
1 2 3 4 5 6 7 8 9 10 11 12
| class Log { private: Log(){ }
public: static void Write(){ } };
|
析构函数
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: float x, y; Entity(){ X = 0.0f; Y = 0.0f; std::cout << "Created Entity !" << std::endl; } void print(){ std::cout << X << "," << Y << std::endl; } ~Entity(){ std::cout << "Destroyed Entity !" << std::endl; } };
void Function(){ Entity e; e.Print(); }
int main(){ Function(); std::cin.get(); }
|
手动调用析构函数
继承
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(){ std::cin.get(); }
|
虚函数
用于实现继承关系中子类(派生类)对父类(基类)方法的重写
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
| 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() {return m_Name; } };
int main(){ Entity* e = new Entity(); std::cout << e->GetName() << std::endl; Player* p = new Player("Cherno"); std::cout << p->GetName() << std::endl;
std::cin.get(); }
|
上述代码还可以写成
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
| 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() {return 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"); PrintName(p); std::cin.get(); }
|
当你运行上述两段代码你会发现,对于Entity均输出了两次,因为总是优先去该类(Entity类里找GetName函数),所以为了让Player和Entity中的GetName各司其职,我们可以引入虚函数,实现覆写(override)需要将基类中的基函数标记为虚函数
于是修改代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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 {return m_Name; } };
|
事实上我们没写override前,依然可以输出我们想要的结果,但是加上override可以提高代码可读性并且方便进行debug,比如函数名拼写错误,会报error(因为基函数里根本没有这个函数)
此处有一种C++语法——初始化列表
初始化列表是 C++ 中的一种语法,用来在构造函数中直接初始化类的成员变量。它的语法是在构造函数的参数列表和函数体之间使用冒号 :
,后面跟随成员变量的初始化方式。
可以提高性能,可以先对类成员变量进行赋值,再用默认构造函数创建一个空字符串。
强制初始化:有些类的成员变量必须通过初始化列表进行初始化。例如:
- const成员变量(不能在构造函数体中赋值,只能在初始化列表中初始化)
- 引用类型成员变量(必须在创建时就被初始化,无法通过赋值修改)
初始化的顺序只与在类中的声明顺序有关,与初始化列表中的顺序无关
ClassName(参数列表) : 类成员1(参数), 类成员2(参数), ... { // 构造函数体 }
关于new
常见用法
1 2 3 4 5
| classname* example = new classname();
int* p = new int(5);
int* arr = new int[10]
|
new与malloc的对比
构造函数和析构函数的调用
new
会调用类对象的构造函数,而 malloc
只分配内存,不会调用构造函数。
- 释放时,
delete
会调用析构函数,而 free
不会调用析构函数。
1 2 3 4 5
| Entity* e1 = new Entity(); delete e1;
Entity* e2 = (Entity*)malloc(sizeof(Entity)); free(e2);
|
类型安全
new
会返回具体类型的指针,不需要进行显式类型转换。
malloc
返回 void*
,需要进行强制类型转换。
1 2
| int* p1 = new int(5); int* p2 = (int*)malloc(sizeof(int));
|
内存分配失败处理
- 如果
new
分配内存失败,会抛出 std::bad_alloc
异常。
malloc
返回 NULL
,需要检查返回值是否为 NULL
。
注:**nothrow
形式**:如果不希望 new
抛出异常,可以使用 nothrow
形式:
1 2 3 4 5
| int* p = new(std::nothrow) int; if (p == nullptr) { std::cout << "Memory allocation failed" << std::endl; }`
|
当内存分配失败时,new会返回nullptr而不是抛出异常
纯虚函数(接口)
允许我们在基类中定义一个没有实现的函数,然后强制子类去实现该函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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 {return m_Name; } };
|
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 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 {return m_Name; } std::string GetClassName() override {return "Player";} };
void Print(Printable* obj) { std::cout << obj->GetClassName() << std::endl; }
int main(){ Entity* e = new Entity(); Player* p = new Player("Cherno"); std::cin.get(); }
|
字符串字面量
字符串字面量是存储在内存中的只读部分的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| int main() { std::string name = "cherno"; name += "hello"; const char* name1 ="cherno"; char name3[] = "cherno"; name3[2] = 'a'; using namespace std::string_literals; std::string name0 = U"cherno"s + U"hello"; const char* example = R"(Line1 Line2 Line3 Line4)"; const char* ex = "Line1\n" "Line2\n" "Line3\n"; }
|
C++中的const
1 2 3
| const int* a int* const a const int* const a;
|
类中的const(下列中的用法只能在类中)
1 2 3 4 5 6 7 8 9 10
| class Entity { private: int m_X, m_Y; public: int GetX() const { return m_X; } };
|
1 2 3 4 5 6 7 8 9 10 11 12
| class Entity { private: int* m_X, m_Y; public:
const int* const GetX() const { return m_X; } };
|
const的高级用法
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 { private: int m_X, m_Y; mutable int var; public: int GetX() const { var = 2; return m_X; } int GetX() { return m_X; } };
void PrintEntity(const Entity& e) {
std::cout << e.GetX() << std::endl; }
int main() { Entity e; }
|
mutable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Entity { private: std::string m_Name; mutable int m_DebugCount = 0; public: const std::string& GetName() const { m_DebugCount++; return m_Name; } };
int main() { const Entity e; e.GetName(); }
|
成员初始化列表
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
| class Entity { private: std::string m_Name; public: Entity() : m_Name("Unknown") { } Entity(const std::string& name) : m_Name(name) { } const std::string& GetName() const {return m_Name; } }
int main() { Entity e0; std::cout << e0.GetName() << std::endl; Entity e1; std::cout << e1.GetName() << std::endl; std::cin.get(); }
|
通过以下代码了解为什么要使用 成员初始化列表
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
| #include <iostream> #include <string>
class Example { public: Example() { std::cout << "Created Entity!" << std::endl; } Entity(int x) { std::cout << "Created Entity with" << x << "!" << std::endl; } };
class Entity { private: std::string m_Name; Example m_Example; public: Entity() { m_Name = std::string("Unknown"); m_Example = Example(8); }
Entity(const std::string& name) : m_Name(name) { } const std::string& GetName() const { return m_Name; } }
int main() { Entity e0; std::cin.get(); }
|
上述代码输出结果为
1 2
| Created Entity! Created Entity with 8!
|
隐式转换implicit与explicit关键字
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
| class Entity { private: std::string m_Name; int m_Age; public: Entity(const std::string& name) : m_Name(name) {} Entity(int age) : m_Name("Unknown"), m_Age(age) };
void PrintEntity(const Entity& entity) { }
int main() { PrintEntity(22); PrintEntity(Entity("Cherno")) Entity a = "Cherno"; Entity b = 22; std::cin.get(); }
|
explicit可以禁用上述的隐式构造,用法:放在构造函数之前
所以上述代码可以变为
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 { private: std::string m_Name; int m_Age; public: explicit Entity(const std::string& name) : m_Name(name) {} explicit Entity(int age) : m_Name("Unknown"), m_Age(age) };
void PrintEntity(const Entity& entity) { }
int main() { PrintEntity(22); PrintEntity(Entity("Cherno")) Entity a = "Cherno"; Entity b = 22; std::cin.get(); }
|
运算符及其重载
没有使用运算符重载前
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
| #include <iostream> #include <string>
struct Vector2 { float x, y; Vector2(float x, float y) : x(x), y(y) {} Vector2 Add(const Vector2& other) const { return Vector2(x + other.x, y+ other.y); } Vector2 Multiply(const Vector2& other) const { return Vector2(x * other.x, y * other.y); } };
int main() { Vector2 position(4.0f, 4.0f); Vector2 speed(0.5f, 1.5f); Vector2 powerup(1.1f, 1.1f); Vector2 result = position.Add(speed.Multiply(powerup)); std::cin.get(); }
|
进行运算符重载
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
| #include <iostream> #include <string>
struct Vector2 { float x, y; Vector2(float x, float y) : x(x), y(y) {} Vector2 Add(const Vector2& other) const { return Vector2(x + other.x, y+ other.y); } Vector2 operator+(const Vector2& other) const { return Add(other); } Vector2 Multiply(const Vector2& other) const { return Vector2(x * other.x, y * other.y); } Vector2 operator*(const Vector2& other) const { return Multiply(other); } bool operator==(const Vector2& other) cosnt { return x == other.x && y == other.y; } bool operator!=(const Vector2& other) cosnt { return !(*this == other); } bool operator==(const Vector2& other) const { return x== other.x && y == other.y; } bool operator!=(const Vector2& other) const { return !(*this == other); } };
std::ostream& operator<<(std::ostream& stream, const Vector2& other) { stream << other.x << ", " << other.y; return stream; }
int main() { Vector2 position(4.0f, 4.0f); Vector2 speed(0.5f, 1.5f); Vector2 powerup(1.1f, 1.1f); Vector2 result1 = position.Add(speed.Multiply(powerup)); Vector2 result2 = position + speed * powerup; if(result1 == result2) std::cout << result2 << std::endl; std::cin.get(); }
|
函数调用也可以进行重载,因为调用方式非常类似于函数调用,所以又称仿函数
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 28 29 30 31 32 33 34 35
| #include <iostream> #include <string>
void PrintEntity(const Entity& e);
class Entity { public: int x, y;
Entity(int x, int y) : x(x), y(y) { this->x = x; this->y = y; Entity& e = *this; PrintEntity(*this); } int GetX() cosnt { const Entity& e = *this; } };
void PrintEntity(const Entity& 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 33 34 35 36 37 38 39 40 41 42
| #include <iostream>
class Entity { public: Entity() { std::cout << "Entity created." << std::endl; } ~Entity() { std::cout << "Entity destroyed." << std::endl; } };
class ScopedPtr { private: Entity* m_Ptr; public: ScopedPtr(Entity* ptr) : m_Ptr(ptr) { }
~ScopedPtr() { delete m_Ptr; } };
int main() { { ScopedPtr e = new Entity(); }
std::cin.get(); }
|
智能指针(smart_ptr)
实现自动化new & delete堆上内存
最简单的智能指针 unique_ptr, unique_ptr不能被复制,因为多个unique_ptr会导致内存被释放后,其他unique_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
| #include <iostream> #include <memory>
class Entity { public: Entity() { std::cout << "Entity created." << std::endl; } ~Entity() { std::cout << "Entity destroyed." << std::endl; } };
int main() { { std::unique_ptr<Entity> entity = std::make_unique<Entity>(); } std::cin.get(); }
|
shared_ptr 可以实现引用计数,只有当引用次数为0之后,才会释放堆上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 25 26 27 28
| #include <iostream> #include <memory>
class Entity { public: Entity() { std::cout << "Entity created." << std::endl; } ~Entity() { std::cout << "Entity destroyed." << std::endl; } };
int main() { { std::shared_ptr<Entity> e0; { std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>(); e0 = sharedEntity; } } std::cin.get(); }
|
weak_ptr 类似于shared_ptr ,但weak_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 28
| #include <iostream> #include <memory>
class Entity { public: Entity() { std::cout << "Entity created." << std::endl; } ~Entity() { std::cout << "Entity destroyed." << std::endl; } };
int main() { { std::weak_ptr<Entity> e0; { std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>(); e0 = sharedEntity; } } 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 39 40 41 42 43 44 45 46 47 48 49 50
| #include <iostream> #include <string>
struct 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() { delete[] m_Buffer; } char& operator[](unsigned int index) { 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; }
int main() { String string = "Cherno"; String second = string; second[2] = 'a'; std::cout << string << std::endl; std::cout << second << 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 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
| #include <iostream> #include <string>
struct 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) { std::cout << "Copied String!"; m_Buffer = new char[m_Size + 1]; memcpy(m_Buffer, other.m_Buffer, m_Size + 1); } ~String() { delete[] m_Buffer; } char& operator[](unsigned int index) { 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; }
void PrintString(String string) { std::cout << string << std::endl; }
int main() { String string = "Cherno"; String second = string; second[2] = 'a'; PrintString(string); PrintString(second); std::cin.get(); }
|
结果输出——事实上PrintString函数在使用时进行了两次拷贝,可以通过引用来避免
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 39 40 41 42 43 44
| #include <iostream> #include <string>
class Entity { public: int x; public: void Print() const { std::cout << "Hello!" << std::endl; } };
class ScopedPtr { private: Entity* m_Obj; public: ScopedPtr(Entity* entity) : m_Obj(entity) { } ~ScopedPtr() { delete m_Obj; } Entity* operator->() { return m_Obj; } const Entity* operator->() const { return m_Obj; } };
int main() { const ScopedPtr entity = new Entity(); entity->Print(); std::cin.get(); }
|
通过箭头操作符来获得内存中某个值的偏移量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <iostream> #include <string>
struct Vector3 { float x, y, z; };
int main() { int offset = (int)&((Vector3*)nullptr)->z; std::cout << offset << 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
| #include <iostream> #include <string>
struct Vertex { float x, y ,z; };
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({ 4, 5, 6 }); 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(); }
|