enable_shared_from_this
Question:有一个类,这个类的对象是使用shared_ptr 管理的,在类内某个成员函数里,我们需要获取指向当前对象的 shared_ptr,应该如何获取?
可能的答案 1:
123456class A {public: std::shared_ptr<A> get_ptr() { return std::shared_ptr<A>(this); // 返回 this 指针的 shared_ptr }};
但是这样做是不安全的。在 get_ptr 中,会根据 this 指针创建一个完全独立的新的std::shared_ptr,这个 shared_ptr 有自己的引用计数,完全独立于当前类对象的 shared_ptr;这时这两个 shared_ptr 是同时在管理这个对象,如果新的这个shared_ptr 计数清零,它会尝试删除这个对象,造成未定义的行为。
正确的做法就是使用std::enable_shared_from_this
std::enable_shared_from_this是一个模板类,可以 ...
与copilot的对话1
wanghao-d: typeid(j).name()是什么
GitHub Copilot: typeid(j).name()是C++中的一个运算符,它返回一个指向类型信息的字符串的指针。在这个例子中,它返回变量j的类型的名称。
wanghao-d: 那 m 是什么
GitHub Copilot: 我不确定你指的是哪个m,因为上下文不清楚。在C++中,m可以是变量名、函数名或类成员变量名。请提供更多上下文或代码以便我能够更好地回答你的问题。
wanghao-d: 我输出typeid(j).name()的值是 m
GitHub Copilot: 根据你提供的代码,typeid(j).name()返回的字符串的第一个字符是m。这是因为j的类型是std::vector<std::vector<int>>,而m是这个类型名称的第一个字符。请注意,typeid返回的字符串的格式和内容是由编译器实现定义的,因此可能会因编译器而异。
wanghao-d: size_t是什么类型
GitHub Copilot: size_t是C++中的一种无符号整数类型,通常用于表示内存大小或 ...
智能指针
std::unique_ptrstd::unique_ptr是一个独占所有权的指针,同一时间只能有一个std::unique_ptr 指向给定对象,当std::unique_ptr被销毁时,它指向的对象也会被释放;
动手试一下
12345678910111213141516#include<iostream>#include<memory>struct A { std::unique_ptr<int> u_ptr; A() { std::cout << "construct A" << std::endl; } ~A() { std::cout << "destruct A" << std::endl; }};int main() { std::unique_ptr<A> u_ptr(new A()); return 0;}
std::share ...
强制类型转换
C++有四种强制类型转换运算符:
static_cast: 可用于基本类型的转换,或者将指针转换为其他基本类型的指针,如:
12int i = 10;double d = static_cast<double>(i);
dynamic_cast: 用于基类和派生类之间的转换,包含了类型检查,只有在确定安全的情况下才会进行转换,否则就会 return nullptr;dynamic_cast 只能用于含有虚函数的类,且只能用于指针或引用。
const_cast: 用于修改类型的 const 和volatile 修饰词,对于 const 变量,可使用 const_cast 去除 const属性,然后才能改变变量的值;
123const int i = 10;int* p = const_cast<int*>(i);*p = 20;
reinterpret_cast: 是一种底层的强制类型转换,可以将任意类型的指针转换为任何其他类型的指针,reinterpret 会对变量进行机器(位)级别的重新解释,不进行任何类型检查和转换,属于非常危险的操作。
最长不重复字符子串
最长不重复字符子串
ACWing: 61.最长不含重复字符的子字符串
LeetCode: 3.无重复字符的最长子串
1234567891011121314151617181920class Solution {public: int lengthOfLongestSubstring(string s) { int ans = 0; std::unordered_map<char, int> hash; for (int i = 0, j = 0; j < s.size(); ++j) { hash[s[j]]++; while (hash[s[j]] > 1) { // i不断前进,直到到了重复字符第一次出现的地方 hash[s[i]]--; i++; } ans = std::max(an ...
求两线段的交点
求两个线段的交点
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273#include <iostream>struct Point { double x; double y;};bool is_intersection(Point A, Point B, Point C, Point D) { // 计算向量 AB 和 AC 的叉积 double c1 = (B.x - A.x) * (C.y - A.y) - (B.y - A.y) * (C.x - A.x); // 计算向量 AB 和 AD 的叉积 double c2 = (B.x - A.x) * (D.y - A.y) - (B.y - A.y) * (D.x - A.x); // 计算向量 CD 和 CA 的叉积 double c3 = (D.x ...
深拷贝和浅拷贝
深拷贝是指,在拷贝对象时复制对象所有的成员变量和成员函数,包括指针指向的动态分配的内存。这样拷贝出来的对象和原对象是完全独立的,对任意一方操作,另一方不会受到影响;
浅拷贝是指,在拷贝对象是只复制对象的成员变量,不会复制指针指向的动态分配的内存。这样拷贝出来的对象,它的指针成员变量和原对象是一块内存,操作任意一方都会修改这块内存,另一方也会受到影响。
所以浅拷贝之后再对任一对象操作,很容易出现内存泄漏或者悬空指针的问题。
C++中的锁
C++中的锁是为了实现多线程编程时线程的同步和互斥机制。在多线程编程中,多个线程可能同时访问一个共享资源,如果没有合适的同步和互斥机制,会出现数据竞争和死锁的问题。
读写锁:std::shared_mutex,读写锁允许多个线程同时对共享资源进行读操作,但是不可以同时进行写操作。当一个线程获取到读锁时,其他线程可以获取读锁,但是无法获取写锁;当一个线程获取到写锁时,其他线程读锁和写锁都无法获取。
互斥锁:std::mutex,互斥锁就是基本的锁,保证同一时间只有一个线程访问共享资源,当一个线程获取互斥锁时,其他线程不能获取锁,直到该线程锁释放。
条件变量:条件变量主要用于线程间通信,可以使用wait让一个线程等候另一个线程的通知,当要等候的线程准备完成后,可以使用notify_one/notify_all来通知等待的线程;条件变量可以通过 std::condition_variable 类来实现。
map容器的实现
一、map 容器的实现
map 容器底层的实现是红黑树,内部是按照键值排序的(无序的是 unordered_map)。map的实现保证插入、查找和删除时间复杂度是O(log n),而unordered_map则可以实现常数时间操作(内部由 hash table 实现)。
红黑树是一种自平衡二叉查找树,保证插入、查找和删除时间复杂度是O(log n)。
红黑树节点要么是红色,要么是黑色;
红黑树根节点是黑色的;
每个叶子节点都是黑色的;
如果一个节点是红色的,那么它的两个叶子节点是黑色的;
对于每个节点,从该节点到它的所有后代叶子的简单路径上,都包含相同数量的黑色节点。
红黑树的平衡性通过对节点的颜色的调整来实现的。当插入或删除节点时,会破坏红黑树的平衡性,那么就需要旋转和重新分配颜色。
了解vector容器
一、迭代器
在遍历容器时删除元素会导致迭代器失效,这是因为删除元素时改变了容器内元素的位置,从而让迭代器指向的元素位置不再有效。
如果要在遍历时删除元素,可以使用 erase 函数,erase 函数会返回指向被删除元素的下一个元素的迭代器,因此不会使迭代器失效。
12345678910111213#include <vector>int main() { std::vector<int> n{1,2,3,4,5}; for (auto it = n.begin(); it != n.end();) { if (*it % 2 == 0) { it = n.erase(it) } else { ++it; } }}