b站Cherno的课[41]-[44]
一、C++运算符及其重载
运算符:一种符号,通常代替一个函数来执行一些事情,(函数)
dereference逆向引用运算符,箭头运算符,+=运算符,用于内存地址的&运算符,左移运算符<<,new,delete,逗号运算符,(),[]
运算符重载:添加参数、创建,允许在程序中定义或更改运算符的行为,Java中不支持,(用得少)
#include <iostream>
#include <string>
//Java写法
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.0f);
Vector2 powerup(1.1f, 1.1f);
Vector2 result = position.Add(speed.Multiply(powerup));
std::cin.get();
}
#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 *this + other;
//return operator+(other);
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);
}
};
int main()
{
Vector2 position(4.0f, 4.0f);
Vector2 speed(0.5f, 1.0f);
Vector2 powerup(1.1f, 1.1f);
Vector2 result1 = position.Add(speed.Multiply(powerup));
Vector2 result2 = position + speed;/** powerup;*/
std::cin.get();
}
#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);
}
};
int main()
{
Vector2 position(4.0f, 4.0f);
Vector2 speed(0.5f, 1.0f);
Vector2 powerup(1.1f, 1.1f);
Vector2 result1 = position.Add(speed.Multiply(powerup));
Vector2 result2 = position + speed * powerup;
std::cin.get();
}
<<左移运算符
#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);
}
};
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;
std::cout << result2 << std::endl;
std::cin.get();
}
友元函数
允许一个非成员函数访问类的私有(private)和保护(protected)成员。
类比:你的朋友被允许进入你家卧室(私有区域),而普通客人只能待在客厅(公共区域)。
class Vector {
private:
int x, y;
public:
Vector(int x, int y) : x(x), y(y) {}
// 声明友元函数
friend std::ostream& operator<<(std::ostream& os, const Vector& v);
};
// 定义友元函数(无需类作用域::)
std::ostream& operator<<(std::ostream& os, const Vector& v) {
os << "(" << v.x << ", " << v.y << ")"; // 现在可以访问私有成员!
return os;
}
// 使用
Vector v(3, 4);
std::cout << v; // 输出 (3, 4)
友元函数是类的“特权朋友”,谨慎授予权限。
适用场景:运算符重载、跨类协作等特殊需求。
核心原则:优先用成员函数和公有接口,必要时再用友元。
tostring函数
在java C#中经常被用来重写
bool operator==(const Vector2& other) const
{
return x == other.x && y == other.y;
[添加链接描述](https://en.cppreference.com/w/cpp/language/operators) }
bool operator!=(const Vector2& other) const
{
return !(*this == other);
//return !operator==(other);
}
if (result1 == result2)
std::cout << "Both results are equal!" << std::endl;
总结
运算符重载让自定义类型与内置类型行为一致,提升代码可读性。
遵循惯例:保持运算符的天然语义,避免反直觉设计。
慎用重载:仅在逻辑明确时使用,避免过度工程化。
二、C++中的this关键字
this关键字:可以访问成员函数,属于某个类的函数或者方法
在方法内部可以引用this
this是一个指向当前实例的指针,该方法属于这个对象实例
定义:this是一个常量指针,指向当前对象的内存地址。
作用:在成员函数中访问对象的成员变量和成员函数。
存在性:所有非静态成员函数(包括const函数)都隐含this指针,无需显式声明。
#include <iostream>
#include <string>
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);
//delete this;
}
int GetX() const
{
const Entity& e = *this;
}
};
void PrintEntity(Entity* e)
{
//Print
}
int main()
{
std::cout << "Hello World!" << std::endl;
std::cin.get();
}
this,指向当前对象的指针。
也就是说这个指针指向当前的对象
#include<iostream>
//我们创建一个player类,用x,y来表示其坐标
class Player
{
int x,int y;
//再创造一个方法
Fuzhi(int a,int b)
{
(*this).x=a;
(*this).y=b;
}
}
int main()
{
Player biubiu_master;//这里我们创建一个名为biubiu之主的玩家类型
biubiu_master.Fuzhi(114,514);
//注意看我们对biubiu之主对象使用Fuzhi这个方法的时候,回去看看这个Fuzhi方法,它里面做的事情是这个: (*this).x=a;(*this).y=b;
//现在调用函数的对象是biubiu_master这个对象,所以方法当中的this指针指向biubiu之主这个对象,所以这个时候(*this),就是biubiu之主了
return 0
}
三、C++的对象生存周期(栈作用域生存期)
在C++中,对象的栈作用域生存期(Stack Scope Lifetime)是指对象在其定义的作用域内(如函数、代码块)自动创建和销毁的过程。这是C++管理内存和资源的核心机制之一,尤其与RAII(Resource Acquisition Is Initialization)原则密切相关。
栈作用域生存期的核心规则
1、创建时机:对象在定义时调用构造函数。
2、销毁时机:对象在离开其作用域时自动调用析构函数。
3、存储位置:对象内存分配在**栈(Stack)**上(非静态局部变量)。
4、顺序原则:构造顺序与定义顺序一致,析构顺序相反(后进先出)。
栈:一种数据结构,可以在上面堆叠一些东西
基于栈的变量和基于堆的变量在对象生存期上的区别
基于栈的变量:在一出作用域就被释放、被摧毁
#include <iostream>
#include <string>
class Entity
{
public:
Entity()
{
std::cout << "Created Entity!" << std::endl;
}
~Entity()
{
std::cout << "Destoryed Entity!" << std::endl;
}
};
int main()
{
{ //堆分配
Entity* e = new Entity();
//栈分配
//Entity e;
}
std::cin.get();
}
#include <iostream>
#include <string>
class Entity
{
public:
Entity()
{
std::cout << "Created Entity!" << std::endl;
}
~Entity()
{
std::cout << "Destoryed Entity!" << std::endl;
}
};
int main()
{
{ //堆分配
//Entity* e = new Entity();
//栈分配
Entity e;
}
std::cin.get();
}
作用:类的作用域
比如智能指针smart_ptr unique_ptr
作用域指针 或作用域锁 scoped_lock
作用域指针 :基本上是一个类,是一个指针的包装器,在构造时用堆分配指针,析构时删除指针
#include <iostream>
#include <string>
class Entity
{
public:
Entity()
{
std::cout << "Created Entity!" << std::endl;
}
~Entity()
{
std::cout << "Destoryed Entity!" << 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();
}
总结
栈作用域生存期是C++自动管理对象生命周期的核心机制。
核心原则:对象的生命周期严格绑定到其作用域,构造和析构自动调用。
关键优势:避免内存泄漏、简化代码、确保异常安全。
注意事项:避免返回悬空引用/指针,谨慎处理跨作用域的对象传递。
mutex locking 互斥锁
如果想锁定一个函数,以便多个线程可以同时访问它而不会爆炸,可以有一个自动作用域锁定。在函数开头锁定,在函数结束解锁。线程很快会出现
核心功能:防止多个线程同时访问共享资源,避免数据竞争(Data Race)。
类比:储物柜的每个柜子一次只能被一个人使用,使用时上锁,用完后解锁。
#include <mutex>
#include <thread>
std::mutex mtx; // 全局互斥锁
int sharedData = 0; // 共享资源
void safeIncrement() {
mtx.lock(); // 加锁
sharedData++; // 安全操作共享数据
mtx.unlock(); // 解锁
}
int main() {
std::thread t1(safeIncrement);
std::thread t2(safeIncrement);
t1.join();
t2.join();
// 最终 sharedData = 2
}
常见问题:死锁Deadlock、递归锁(std::recursive_mutex)
四、C++的智能指针
智能指针:本质:一个原始指针的包装
当创建一个智能指针,会调用new并为你分配内存,然后基于你使用的智能指针,这些内存会在某一时刻自动释放。
unique_ptr 作用域指针
超出作用域时会被销毁,然后调用delete,不能复制、共享
#include <iostream>
#include <string>
#include <memory>
class Entity
{
public:
Entity()
{
std::cout << "Created Entity!" << std::endl;
}
~Entity()
{
std::cout << "Destoryed Entity!" << std::endl;
}
void Print(){}
};
int main()
{
{
std::unique_ptr<Entity> entity = std::make_unique<Entity>();
entity->Print();
}
std::cin.get();
}
shared_ptr 共享指针
实现方式取决于编译器和你在编译器中使用的标准库
通常:引用计数
引用计数是一种方法,可以跟踪你的指针有多少个引用
一旦引用计数达到零,就被删除
#include <iostream>
#include <string>
#include <memory>
class Entity
{
public:
Entity()
{
std::cout << "Created Entity!" << std::endl;
}
~Entity()
{
std::cout << "Destoryed Entity!" << std::endl;
}
void Print(){}
};
int main()
{
{
std::shared_ptr<Entity> e0;
{
std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>();
//std::shared_ptr<Entity> sharedEntity(new Entity());
//std::shared_ptr<Entity> e0 = sharedEntity;
e0 = sharedEntity;
}
}
std::cin.get();
}
shared_ptr 需要分配另一块内存,叫做控制块,用来存储引用计数
weak_ptr 弱指针
int main()
{
{
std::shared_ptr<Entity> e0;
{
std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>();
//不会增加引用计数
std::weak_ptr<Entity> weakEntity = sharedEntity;
e0 = sharedEntity;
}
}
std::cin.get();
}
软链接和硬链接
两种不同的文件引用方式。它们的核心区别在于如何指向文件数据
硬链接是数据的“分身钥匙”,删除原文件不影响其他钥匙。
软链接是路径的“路标牌”,原文件消失后路标失效。
选择原则:
需要数据保护 → 硬链接;
需要灵活引用 → 软链接。
总结:
优先使用unique_ptr 作用域指针,较低开销
如果需要在对象之间共享,不能使用unique_ptr时,使用shared_ptr