当前位置: 首页 > news >正文

Cpp学习手册-进阶学习

C++标准库和C++20新特性

C++标准库概览:

核心库组件介绍:

容器:

C++ 标准库提供了多种容器,它们各有特点,适用于不同的应用场景。

  • std::vector

    vector:动态数组,支持快速随机访问。

    #include <iostream>
    #include <vector>int main(){// vectorstd::vector<int> vec = {1,2,3,4,5};for (auto &item:vec) {item *= 2;}for (auto &item:vec) {std::cout << item << ' ';}return 0;
    }
    

    对于上面的代码我存在一些疑问:

    • auto 有什么用:

      auto 是一个类型说明符,它允许编译器自动推断变量的类型。

      就以上面这个为例:auto &item:vec 这里的 vec 是一个 std::vector<int> 对象,所以这里的 item 会被自动解析为 int& 类型。这表示 item 指向 vec 存储的某块内存的其中一个引用即指向当前元素的引用。

      使用引用的话,不需要重新复制元素,提高性能。

  • std::map

    map:存储键值对,基于红黑树实现。

    #include <map>
    int main(){// mapstd::map<std::string,int> mapList = {{"caiping",30},{"chengfeng",20}};// map 赋值mapList["qianwei"] = 24;// map 循环for (const auto &[key,value] : mapList) {std::cout << "key: " << key << " " << "value: " << value << std::endl;}return 0;
    }
    
    • const auto &[key,value] : mapList 是什么用法?有什么用?

      这里采取的是结构化绑定,auto 其实跟上面的用法一样,std::map<std::string,int> mapList 这里的 mapList 是一个对象,它存储的是一个键值对,键是 std::string 类型,值是 int 类型。

      相当于这里的 auto &[key,value] 拆分了 mapList 对象。这里的 const 的作用是它是一个修饰符,表示这里的引用是只读的,你不能通过它们修改原始数据。

  • std::list

    #include <list>
    // std::list
    int main(){std::list <int> intList = {1,2,3,4,5};// 这里的 it 代表的是迭代器,在这里指向的是第一个元素auto it = intList.begin();++it; // 即指向第二个元素intList.insert(it,10); // 在第二个位置插入 10std::cout << "origin: ";for(const auto& item:intList){std::cout << item << " ";}std::cout << std::endl;++it;  // 移动到第二个元素intList.erase(it);  std::cout << "after: ";for(const auto& item:intList){std::cout << item << " ";}return 0;
    }
    

    输出结果

    origin: 1 10 2 3 4 5
    after: 1 10 2 4 5
    

    通过输出结果我们可以观察得出:

    迭代器 it 指向在开始的时候确实是指向第二个元素的即 2 , 然后我们插入了一个元素之后在 2 之前插入的 10 这也是正确的,但是我们发现我们再次执行 ++it 之后去删除元素之后打印出来的结果发现 3 被删除了,并不是 2 被删除,表示 it 迭代器指向的还是之前的元素 2 的,然后执行 ++it 之后,指向了 3 ,所以删除的是 3

  • std::unordered_map

    #include <unordered_map>// unordered_map
    int main() {std::unordered_map<std::string, int> mapList = {{"apple",  3},{"banana", 5},{"orange", 2}};// 插入数据mapList["grape"] = 4;// 查找元素auto it = mapList.find("grape");if (it != mapList.end()) {std::cout << "Found grape: " << it->second << '\n';} else {std::cout << "grape not found\n";}// 遍历并打印所有元素for (const auto& [key, value] : mapList) {std::cout << key << ": " << value << '\n';}return 0;
    }
    

    输出结果

    grape not found
    orange: 2
    banana: 5
    grape: 4
    apple: 3
    

    这里我们需要注意的是

    it != mapList.end() 这个判断为什么没有问题呢?我们findgrape不就是在最后一项吗?

    • mapList.end() 是一个尾后迭代器,不指向任何实际的元素,而是表示遍历的终点
    • it != mapList.end() 用于检查 find 是否成功找到指定的键。无论 "grape" 在容器中的实际位置如何,只要 find 成功找到它,it 就不会等于 mapList.end()
    • std::unordered_map 不保证任何特定的顺序,所以 "grape" 可能不在容器的 “最后” 位置。

    这里因为使用的是迭代器所以使用的是 it->second用来指向 value

  • std::set

    #include <set>
    // std::set
    int main(){std::set<int> setList = {5, 3, 1, 4, 2};setList.insert(6);for(auto & item : setList){std::cout << item << " ";}return 0;
    }
    

    输出结果

    1 2 3 4 5 6
    
  • std::unordered_set

    #include <unordered_set>
    // std::unordered_set
    int main(){std::unordered_set<int> setList = {5, 3, 1, 4, 2};setList.insert(6);for(auto & item : setList){std::cout << item << " ";}return 0;
    }
    

    输出结果

    6 2 4 1 3 5
    

    对于 setunordered_set 来说最主要的特征是存储的元素都是唯一的,只是说 set 是有序的而unordered_set 是无序的。

总结:

std::vector 基于动态数组构造,因此支持随机访问,并且因为是动态的所以会自动管理内存。

std::list 基于双向列表,插入和删除操作快,但不支持随机访问。

std::map 基于红黑树实现,按键排序,提供对数时间复杂度的查找、插入和删除。

std::unordered_map 基于哈希表实现,平均常数时间复杂度的查找、插入和删除。

std::set 基于红黑树实现,存储唯一元素并按顺序排列。

std::unordered_set 基于哈希表实现,存储唯一元素,无序。

算法:
  • std::sort:对范围内的元素进行排序。
  • std::find:在范围内查找指定值。
  • std::transform:将一个范围内的每个元素通过函数转换后存储到另一个范围。
  • std::for_each:对范围内的每个元素应用一个函数。
  • std::copy:将一个范围内的元素复制到另一个范围。
  • std::remove:移除范围内的指定值(不会真正删除元素,而是移动到范围末尾)。
举例:
#include <algorithm>int main() {std::vector<int> vec = {5, 2, 9, 1, 7, 3, 8, 4, 6};// std::sortstd::sort(vec.begin(), vec.end());std::cout << "Sorted: ";for (auto &item: vec) {std::cout << item << " ";}std::cout << std::endl;// std::findauto it = std::find(vec.begin(), vec.end(), 7);if (it != vec.end()) {// std::distance(vec.begin(), it) 用于计算std::cout << "Found 7 at position: " << std::distance(vec.begin(), it) << std::endl;} else {std::cout << "7 not found" << std::endl;}// std::transform// 定义一个初始长度的动态数组std::vector<int> increase_vec(vec.size());std::transform(vec.begin(), vec.end(), increase_vec.begin(), [](int n) { return n + 2; });std::cout << "increased: ";for (auto & item: increase_vec) {std::cout << item << " ";}std::cout << std::endl;// std::for_eachstd::cout << "Printing each element: ";std::for_each(increase_vec.begin(),increase_vec.end(),[](int item){   std::cout << item << " ";});std::cout << std::endl;// std::copystd::vector<int> copy_vec;// std::back_inserter是用于创建一个特殊的输出迭代器。它可以被用来在容器的末尾添加元素,类似于调用容器的 push_back 方法。// 具体来说,std::back_inserter(copy_of_numbers) 实际上返回的是一个 std::back_insert_iterator 对象,// 这个对象重载了赋值操作符(operator=),使得每次向它赋值时都会调用目标容器的 push_back 方法来添加新的元素。std::copy(vec.begin(),vec.end(), std::back_inserter(copy_vec));std::cout << "Printing each element of copy_vec: ";std::for_each(copy_vec.begin(),copy_vec.end(),[](int item){   std::cout << item << " ";});std::cout << std::endl;// std::remove// 使用 std::remove 移除所有值为 3 的元素it = std::remove(copy_vec.begin(), copy_vec.end(), 3);// 注意:这不会改变容器的实际大小,需要手动调整大小或删除多余的元素copy_vec.erase(it, copy_vec.end()); // 删除从 it 到末尾的元素std::cout << "After removing 3: ";for (int num : copy_vec) std::cout << num << " ";std::cout << std::endl;return 0;
}
输出结果:
Sorted: 1 2 3 4 5 6 7 8 9
Found 7 at position: 6
increased: 3 4 5 6 7 8 9 10 11
Printing each element: 3 4 5 6 7 8 9 10 11
Printing each element of copy_vec: 1 2 3 4 5 6 7 8 9
After removing 3: 1 2 4 5 6 7 8 9
注意点:

这里 std::back_inserter 在我们 std::copy 的时候经常会配合使用,它对应的具体的一些补充上面代码注释也写了。

最后这个 std::remove 方法,虽然我们看到的确实做到了删除的效果,但它并不会真的做到删除的效果,实际上它并没有改变容器的大小,也没有释放内存。我现在举一个例子:比如说现在有一个容器 vec = {1, 2, 2, 3, 4, 5}; 在它调用remove方法之后,可能会改变容器的内容为 {1, 3, 4, 5, 2, 2} 或者 {1, 3, 4, 5, 2, 2} 中的任何一种排列,只要所有的 2 被移动到了末尾。it 会指向 5 后面的位置。所以为什么我们为什么在这里remove之后调用 erase 方法来删除 it 到向量末尾之间的所有元素。

智能指针:

C++11引入了智能指针来帮助管理动态分配的对象,从而避免内存泄漏和其他资源管理问题。

  • std::unique_ptr:独占所有权的智能指针,不能被复制,只能被移动。

    对于std::unique_ptr 使用场景:

    1. 当你只需要一个对象的所有者时

      • 当你创建一个对象并且希望只有单一的所有者来管理它的生命周期时,可以使用 std::unique_ptr。这避免了多个所有者同时管理同一个对象导致的复杂性和潜在错误。
      // 案例 1: 单一所有者
      // 假设你有一个数据库连接对象,你希望在整个程序中只有一份这个连接对象,并且确保它在不再需要时被正确释放。
      class DatabaseConnection {
      public:DatabaseConnection() { std::cout << "连接数据库...." << std::endl; }~ DatabaseConnection() { std::cout << "断开连接数据库...." << std::endl; }
      };void useDatabase(std::unique_ptr<DatabaseConnection> databaseConnection){std::cout << "使用数据库连接" << std::endl;
      }int main(){auto db = std::unique_ptr<DatabaseConnection>();// 这样就将连接对象转移到方法useDatabase身上了useDatabase(std::move(db));if(!db){std::cout << "连接对象已转移" << std::endl;}return 0;
      }
      

      输出结果

      使用数据库连接
      连接对象已转移
      
    2. 在函数之间传递所有权,但不想复制对象

      • 当你需要将一个动态分配的对象从一个函数传递到另一个函数,并且不希望进行深拷贝(因为深拷贝可能会很昂贵),你可以使用 std::unique_ptr 来转移所有权。这样,接收方会成为新的唯一所有者。
    3. 作为类成员变量,自动管理类内部的资源

      • 当你的类需要管理一个动态分配的资源时,可以使用 std::unique_ptr 作为成员变量。这样可以确保当类的对象被销毁时,资源会被自动释放,从而避免内存泄漏。
  • std::shared_ptr:共享所有权的智能指针,允许多个指针指向同一个对象,引用计数为零时释放对象。

    对于std::shared_ptr 使用场景:

    1. 当你需要多个所有者共享同一个资源时

      • 当多个对象都需要访问同一个资源,并且每个对象都希望拥有该资源的所有权时,可以使用 std::shared_ptr。这样,只要有一个 std::shared_ptr 指向该资源,资源就不会被释放。
      #include <thread>// 案例 1: 多个所有者共享同一个资源
      // 假设你有一个日志记录器对象,多个线程或模块都需要访问这个日志记录器,并且每个线程或模块都希望拥有该日志记录器的所有权。
      class Logger {
      public:void log(const string &message) {cout << "Log: " << message << "\n";}
      };void threadFunction(shared_ptr<Logger> logger, const string &message) {// 这里为什么不是使用.而是使用->的原因就在于我们这里的logger并不是一个对象实例而是一个智能指针,所以指针要调用对象的方法的话需要使用->logger->log(message);
      }int main() {auto logger = make_shared<Logger>();thread t1(threadFunction, logger, "Thread 1");thread t2(threadFunction, logger, "Thread 2");// 等待线程完成t1.join();t2.join();return 0;
      }
      

      输出结果

      Log: Thread 1
      Log: Thread 2
      

      这里我发现会有可能出现其他情况的输出,我发现是因为并发导致的。

    2. 当资源的所有权需要在多个对象之间传递,而每个对象都需要访问该资源

      • 在某些情况下,资源的所有权可能需要在多个对象之间传递,但每个对象都需要访问该资源。std::shared_ptr 可以方便地实现这一点,因为复制 std::shared_ptr 会增加引用计数,确保资源不会过早释放。

        // 案例 2: 资源所有权在多个对象之间传递
        // 假设你有一个图形库,其中包含多个图形对象(如 Circle 和 Rectangle),并且这些图形对象需要共享同一个绘图上下文(如 GraphicsContext)。
        class GraphicsContext{
        public:void draw(const std::string& shape){cout << "Drawing: " << shape << endl;}
        };class Shape {
        protected:shared_ptr<GraphicsContext> context;
        public:Shape(shared_ptr<GraphicsContext> context): context(context) {}virtual void draw() = 0;
        };class Circle : public Shape {
        public:// 其实这个语法就是将Circle的构造函数的ctx赋值给shape中的contextCircle(std::shared_ptr<GraphicsContext> ctx) : Shape(ctx) {}void draw() override {context->draw("Circle");}
        };class Rectangle : public Shape {
        public:Rectangle(std::shared_ptr<GraphicsContext> ctx) : Shape(ctx) {}void draw() override {context->draw("Rectangle");}
        };int main(){auto context = make_shared<GraphicsContext>();Circle circle(context);Rectangle rectangle(context);circle.draw();rectangle.draw();return 0;
        }
        

        输出结果

        Drawing: Circle
        Drawing: Rectangle
        
    3. 在复杂的对象图中,当多个对象需要引用同一个资源时

      • 在复杂的数据结构或对象图中,可能存在多个对象需要引用同一个资源的情况。使用 std::shared_ptr 可以简化这些引用关系的管理,避免手动管理资源的生命周期。

        // 案例 3: 复杂对象图中的资源共享
        // 假设你有一个树形结构,其中每个节点都包含一个共享的数据块,并且多个节点可能引用同一个数据块。
        #include <vector>
        class DataBlock {
        public:int value;DataBlock(int val):value(val){}
        };class TreeNode {
        private:shared_ptr<DataBlock> data;vector<TreeNode> children;public:TreeNode(shared_ptr<DataBlock> d) :data(d){}void addChild(TreeNode child){children.push_back(child);}void printData() const {cout << "Data:" << data->value << endl;}void printChildrenData() const{for (const auto& item: children) {item.printData();}}
        };int main(){auto shareData = make_shared<DataBlock>(42);TreeNode root(shareData);TreeNode child1(shareData);TreeNode child2(shareData);root.addChild(child1);root.addChild(child2);root.printData();cout << "--------------------------" << endl;root.printChildrenData();return 0;
        }
        

        输出结果

        Data:42
        --------------------------
        Data:42
        Data:42
        
  • std::weak_ptr:不控制对象生命周期的智能指针,用于解决循环引用问题。

    1. 当你需要观察 std::shared_ptr 所管理的对象,但不希望增加引用计数时
      • 有时你需要观察一个由 std::shared_ptr 管理的对象,但不想影响其生命周期。例如,在缓存或观察者模式中,你可能希望在对象仍然存在时访问它,但在对象被销毁后不再访问。
    2. 解决 std::shared_ptr 之间的循环引用问题
      • 当两个或多个 std::shared_ptr 相互引用时,可能会导致内存泄漏,因为每个 std::shared_ptr 都会增加其他 std::shared_ptr 的引用计数,从而永远不会释放资源。使用 std::weak_ptr 可以打破这种循环引用,确保资源能够被正确释放。
    3. 在缓存或观察者模式中,当你需要检查对象是否仍然存在而不影响其生命周期时
      • 在某些设计模式中,如观察者模式或缓存机制,你可能需要存储对对象的弱引用,以便在需要时检查对象是否仍然存在。如果对象已经被销毁,你可以采取相应的措施(如删除缓存条目)。
多线程:

C++11引入了多线程支持,包括线程创建、同步机制和异步任务处理。

  • <thread>:创建和管理线程。这个头文件提供了创建和管理线程的功能。你可以使用std::thread来创建一个新线程,并传递给它一个可调用对象(如函数、lambda表达式等)。当线程运行完毕后,可以使用join()方法等待线程结束,或者使用detach()方法让线程独立于创建它的线程执行。

    #include <iostream>
    #include <thread>void threadFuc(){std::cout << "Hello from a thread!" << std::endl;
    }// thread
    int main(){std::thread t(threadFuc);t.join(); // 等待线程t完成return 0;
    }
    
  • <mutex>:互斥锁,用于保护共享数据。这个头文件提供了一系列的互斥锁类型,用于同步访问共享资源,防止数据竞争。std::mutex是最基本的一种互斥锁,而std::lock_guardstd::unique_lock是用于简化锁定/解锁过程的RAII风格的包装器。

    #include <mutex>std::mutex m;
    int shared_data = 0;void safe_increment() {std::lock_guard<std::mutex> lock(m); // 自动管理锁++shared_data;
    }int main(){std::thread t1(safe_increment), t2(safe_increment);t1.join();t2.join();std::cout << "Shared data: " << shared_data << std::endl;return 0;
    }
    

    这里最重要的是 std::lock_guard<std::mutex> lock(m); 结合 std::mutex m; 使用。

  • <future>:异步计算的结果,可以通过std::asyncstd::promise来获取。

    这个头文件允许你进行异步计算,并且能够获取结果。std::async是一个非常方便的工具,它可以启动一个异步任务,并返回一个std::future对象,该对象可以用来查询任务的状态或获取最终的结果。另外,std::promisestd::future一起工作,可以在不同的线程之间安全地传递结果。

    #include <future>int compute(int x) {return x * x;
    }int main() {std::future<int> result = std::async(compute, 5); // 异步调用computestd::cout << "Result: " << result.get() << std::endl; // 获取结果return 0;
    }
    

C++20新特性:

  • 概念(Concepts):学习如何定义和使用约束来改进模板代码。

    概念允许我们为模板参数指定更具体的约束条件。这不仅增加了代码的可读性,还帮助编译器在早期发现类型错误。

    比如说我们可以定义一个整数类型的概念,在使用的时候直接通过定义好的这个类型并配合关键字 auto 进行调用,如果传入是一个浮点数值,则会出现编译错误。

  • 模块(Modules):探索模块化编程以提高构建效率和代码组织。

    模块旨在替代传统的头文件机制,提供更好的封装性和更快的编译速度。

    比如说我们可以创建一个包含数学方法的模块文件,通过 export 将定义好的模块以及方法暴露出去,在我们程序中想要使用这个模块的内容时,使用 import 导入并直接调用对应的方法即可。

  • 其他特性

    std::span:提供了对数组或容器的一段连续元素的轻量级视图。

    std::format:用于格式化字符串输出

    constexpr if:它允许在编译时根据常量表达式的值选择性地编译代码分支。

模板元编程、类型推导和 constexpr

模板元编程:

基础概念

  • 模板参数:可以是类型、非类型参数(如整数或指针)或模板。

    比如说,template<typename T, int N> class Array {};

  • 特化:为特定类型的模板提供具体的实现。

    比如说,template<> class Array<int, 10> {};

  • 偏特化:对模板的部分参数进行特化,通常用于类模板。

    比如说,template<typename T> class Array<T, 5> {};

这里只做一个简单的描述,后期会出详细的一些讲解。

泛型编程

#include <iostream>template <typename T>
T max(T a, T b) {return (a > b) ? a : b;
}int main(){int a = 1;int b = 2;auto maxValue = max(a,b);std::cout << "max Value is " << maxValue << std::endl;return 0;
}

输出结果

max Value is 2

类型推导:

  • auto:正确地使用auto关键字简化变量声明。

    举个例子

    int main(){std::vector<int> numbers = {1,2,3,4,5,6};for (auto item: numbers) {std::cout << item << " ";}return 0;
    }
    

    在这里 auto 允许编译器自动推断出变量的类型即 item 就是对应的 int 类型。

  • decltype:获取表达式的类型,并用于更复杂的类型推导。

    int main(){int x = 5;const int& y = x;auto z1 = y; // 这里使用 auto 推导出来的 z1 的类型是 intdecltype(y) z2 = y; // 这里如果使用 decltype 推导出来的 z2 的类型是 const int & 类型return 0;
    }
    

constexpr:

  • 常量表达式:编写可以在编译期求值的函数和变量。

  • 编译期计算:利用constexpr进行数学运算和其他计算,减少运行时开销。

    举例

    constexpr int square (int x) {return x * x;
    }constexpr int factorial(int n) {return (n <= 1) ? 1 : (n * factorial(n - 1));
    }int main(){constexpr int result = square(5); // 编译时计算结果std::cout << "result: " << result << std::endl;constexpr int fact_5 = factorial(5); // 在编译时计算阶乘std::cout << "fact_5: " << fact_5 << std::endl;return 0;
    }
    

    输出结果

    result: 25
    fact_5: 120
    

总结:

这一章节我们学会了进阶的 C++ 的理论知识。包括对一些核心库的内容介绍:容器、算法、智能指针、多线程等等。接着我们讲述了 C++20 新特性以及模板元编程、类型推导和 constexpr。接下来我会做一个实战来练习一下我们之前所学的内容,实现一个高性能数值计算库。

相关文章:

Cpp学习手册-进阶学习

C标准库和C20新特性 C标准库概览&#xff1a; 核心库组件介绍&#xff1a; 容器&#xff1a; C 标准库提供了多种容器&#xff0c;它们各有特点&#xff0c;适用于不同的应用场景。 std::vector&#xff1a; vector&#xff1a;动态数组&#xff0c;支持快速随机访问。 #in…...

代码随想录-字符串-反转字符串中的单词

题目 题解 法一:纯粹为了做出本题&#xff0c;暴力解 没有技巧全是感情 class Solution {public String reverseWords(String s) {//首先去除首尾空格s s.trim();String[] strs s.split("\\s");StringBuilder sb new StringBuilder();//定义一个公共的字符反转…...

勒索软件通过易受攻击的 Cyber​​Panel 实例攻击网络托管服务器

一个威胁行为者&#xff08;或可能多个&#xff09;使用 PSAUX 和其他勒索软件攻击了大约 22,000 个易受攻击的 Cyber​​Panel 实例以及运行该实例的服务器上的加密文件。 PSAUX 赎金记录&#xff08;来源&#xff1a;LeakIX&#xff09; Cyber​​Panel 漏洞 Cyber​​Pane…...

Open WebUI + openai API / vllm API ,实战部署教程

介绍Open WebUI + Ollama 的使用: https://www.dong-blog.fun/post/1796 介绍vllm 的使用:https://www.dong-blog.fun/post/1781 介绍 Ollama 的使用: https://www.dong-blog.fun/post/1797 本篇博客玩个花的,Open WebUI 本身可以兼容openai 的api, 那来尝试一下。 仅供…...

InsuranceclaimsController

目录 1、 InsuranceclaimsController 1.1、 保险理赔结算 1.2、 生成预约单号 1.3、 保存索赔表 InsuranceclaimsController using QXQPS.Models; using QXQPS.Vo; using System; using System.Collections; using System.Collections.Generic; using System.Li…...

如何成为开源代码库Dify的contributor:解决issue并提交PR

前言 Dify 是一个开源的大语言模型&#xff08;LLM&#xff09;应用开发平台&#xff0c;它融合了后端即服务&#xff08;Backend as Service&#xff09;和LLMOps的理念&#xff0c;旨在简化和加速生成式AI应用的创建和部署。Dify提供了一个用户友好的界面和一系列强大的工具…...

SQL进阶技巧:巧用异或运算解决经典换座位问题

目录 0 问题描述 1 数据准备 2 问题分析 2.1 什么是异或 2.2异或有什么特性? 2.3 异或应用 2.4 本问题采用异或SQL解决方案 3 小结 0 问题描述 表 seat中有2个字段id和student id 是该表的主键(唯一值)列,student表示学生姓名。 该表的每一行都表示学生的姓名和 ID。…...

【MySQL】 运维篇—数据库监控:使用MySQL内置工具(如SHOW命令、INFORMATION_SCHEMA)进行监控

随着应用程序的增长&#xff0c;数据库的性能和稳定性变得至关重要。监控数据库的状态和性能可以帮助数据库管理员&#xff08;DBA&#xff09;及时发现问题&#xff0c;进行故障排查&#xff0c;并优化数据库的运行效率。通过监控工具&#xff0c;DBA可以获取实时的性能指标、…...

【温酒笔记】DMA

参考文档&#xff1a;野火STM32F103 1. Direct Memory Access-直接内存访问 DMA控制器独立于内核 是一个单独的外设 DMA1有7个通道DMA2有5个通道DMA有四个等级&#xff0c;非常高&#xff0c;高&#xff0c;中&#xff0c;低四个优先级如果优先等级相同&#xff0c;通道编号越…...

力扣判断字符是否唯一(位运算)

文章目录 给一个数n,判断它的二进制位中第x位是0还是1(从0开始计数)将一个数n的二进制位第X位修改为1(从0开始计数)将一个数n的二进制第x位修改为0(从0开始计数)提取一个数n二进制中最右侧的1去掉一个数n二进制表示中最右侧的1 今天我们通过判断字符是否唯一这个题来了解位运算…...

GPU和CPU区别?为什么挖矿、大模型都用GPU?

GPU(图形处理单元)和CPU(中央处理单元)是计算机中两种不同类型的处理器&#xff0c;它们在设计和功能上有很大的区别。 CPU是计算机的大脑&#xff0c;专门用于执行各种通用任务&#xff0c;如操作系统管理、数据处理、多任务处理等。它的架构设计旨在适应多种任务&#xff0c…...

新兴斗篷cloak技术,你了解吗?

随着互联网技术的飞速发展&#xff0c;网络营销领域也经历了翻天覆地的变革。 从最早的网络横幅广告到如今主流的搜索引擎和社交媒体营销&#xff0c;广告形式变得越来越多样。 其中&#xff0c;搜索引擎广告一直以其精准投放而备受青睐&#xff0c;但近年来&#xff0c;一项名…...

【抽代复习笔记】34-群(二十八):不变子群的几道例题

例1&#xff1a;证明&#xff0c;交换群的任何子群都是不变子群。 证&#xff1a;设(G,o)是交换群&#xff0c;H≤G&#xff0c; 对任意的a∈G&#xff0c;显然都有aH {a o h|h∈H} {h o a|h∈H} Ha。 所以H⊿G。 【注&#xff1a;规范的不变子群符号是一个顶角指向左边…...

Chrome和Firefox如何保护用户的浏览数据

在当今数字化时代&#xff0c;保护用户的浏览数据变得尤为重要。浏览器作为我们日常上网的主要工具&#xff0c;其安全性直接关系到个人信息的保密性。本文将详细介绍Chrome和Firefox这两款主流浏览器如何通过一系列功能来保护用户的浏览数据。&#xff08;本文由https://chrom…...

CentOS 7镜像下载

新版本系统镜像下载&#xff08;当前最新是CentOS 7.4版本&#xff09; CentOS官网 官网地址 http://isoredirect.centos.org/centos/7.4.1708/isos/x86_64/ http://mirror.centos.org/centos/7/isos/ 国内的华为云&#xff0c;超级快&#xff1a;https://mirrors.huaweiclou…...

opencv-windows-cmake-Mingw-w64,编译opencv源码

Windows_MinGW_64_OpenCV在线编译动态库,并使用在C项目: (mingw-w64 cmakegithub actions方案) 修改版opencv在线编译: 加入opencv-contrib库, 一起编译生成动态库,在线编译好的opencv动态库,可以下载使用.验证opencv动态库是否可用的模板项目,测试opencv动态库是否可用的模板…...

Puppeteer点击系统:解锁百度流量点击率提升的解决案例

在数字营销领域&#xff0c;流量和搜索引擎优化&#xff08;SEO&#xff09;是提升网站可见性的关键。我开发了一个基于Puppeteer的点击系统&#xff0c;旨在自动化地提升百度流量点击率。本文将介绍这个系统如何通过模拟真实用户行为&#xff0c;优化关键词排名&#xff0c;并…...

Kyber原理解析

Kyber是一种IND-CCA2安全的密钥封装机制。Kyber的安全性基于在模格&#xff08;MLWE问题&#xff09;中解决LWE问题的难度。Kyber的构造采⽤两阶段⽅法&#xff1a;⾸先介绍⼀种⽤来加密固定32字节⻓度的消息原⽂的IND-CPA安全性的公钥加密⽅案&#xff0c;我们称之为 CPAPKE&a…...

2024 CCF CSP-J/S 2024 第二轮认证 真题试卷

2024年信息学奥赛CSP-J2入门级复赛真题试卷 题目总数&#xff1a;4 总分数&#xff1a;400 编程题 第 1 题 问答题 扑克牌(poker) 【题目描述】 小 P 从同学小 Q 那儿借来一副 n 张牌的扑克牌。 本题中我们不考虑大小王&#xff0c;此时每张牌具有两个属性:花色和…...

Android 无障碍服务常见问题梳理

android 无障碍服务本意是为了帮助盲人操作手机而设计&#xff0c;但是现在也有人利用这个做自动化操作。 本片文章讲述的主要用作自动化方面。 官方文档 关于配置方法和接口列表&#xff0c;参考 无障碍 比较常用的接口&#xff1a; 1. 执行点击操作 2. 触摸屏幕&#xf…...

Milvus 与 Faiss:选择合适的向量数据库

向量数据库 Milvus 和 Faiss 都是处理大规模向量数据的工具&#xff0c;尤其适用于需要相似性搜索的场景&#xff0c;比如推荐系统、图像检索和自然语言处理等。但它们各自的设计初衷和功能有所不同&#xff0c;适用于不同的使用场景。下面&#xff0c;我们从性能、功能特性、部…...

2024最全CTF入门指南、CTF夺旗赛及刷题网站(建议收藏!)

文章目录 一、赛事介绍二、竞赛模式三、CTF各大题型简介四、赛题情况分析CTF 工具集合Web | Web 安全&#x1f578; MISC | 杂项❆ 基础工具❆ 解题工具❆ 开源脚本&#x1f511; Crypto | 密码学 &#x1f4ab; Reverse | 逆向基础工具&#x1f4a5; PWN | 二进制 &#x1f44…...

【论文阅读】ESRGAN+

学习资料 论文题目&#xff1a;进一步改进增强型超分辨率生成对抗网络&#xff08;ESRGAN : FURTHER IMPROVING ENHANCED SUPER-RESOLUTION GENERATIVE ADVERSARIAL NETWORK&#xff09;论文地址&#xff1a;2001.08073代码&#xff1a;ncarraz/ESRGANplus&#xff1a; ICASSP …...

北京市首发教育领域人工智能应用指南,力推个性化教育新篇章

近年来&#xff0c;人工智能在全球教育领域的应用呈现蓬勃发展之势&#xff0c;各国都在探索如何将其更好的融入教育体系&#xff0c;在这一背景下&#xff0c;北京市于10月26日发布《北京市教育领域人工智能应用指南》&#xff08;以下简称《指南》&#xff09;&#xff0c;推…...

【Java并发编程】信号量Semaphore详解

一、简介 Semaphore&#xff08;信号量&#xff09;&#xff1a;是用来控制同时访问特定资源的线程数量&#xff0c;它通过协调各个线程&#xff0c;以保证合理的使用公共资源。 Semaphore 一般用于流量的控制&#xff0c;特别是公共资源有限的应用场景。例如数据库的连接&am…...

window11使用wsl2安装Ubuntu22.04

目录 1、快速了解wsl2 安装子系统linux流程&#xff08;B站视频&#xff09; 2、wsl2常用命令 3、windows与子系统Linux文件访问方法 4、子系统linux使用windows网络代理、网络配置&#xff08;镜像网络&#xff0c;非NAT&#xff09; 5、wsl2 Ubuntu miniconda 安装 6、…...

虚拟滚动 - 从基本实现到 Angular CDK

简介 在大数据列表的处理上&#xff0c;虚拟滚动是一种优化性能的有效方式。本篇文章将详细介绍两种常见的虚拟滚动实现方式&#xff1a;使用 transform 属性和 Intersection Observer。重点讲解如何通过 transform 属性实现高效的虚拟滚动&#xff0c;并对比Angular CDK中的实…...

Spring WebFlux学习笔记(一)

核心思想 WebFlux主要是异步 例子 参考一个源码&#xff1a; https://blog.csdn.net/qq_43923045/article/details/106309432?spm1001.2014.3001.5506 GetMapping("/delay1")public Mono<RestResult> delayResult() {long l System.currentTimeMillis();…...

富格林:正确追损思维安全交易

富格林指出&#xff0c;对于如何正确追损的这个问题是需要持续付出时间和精力的&#xff0c;发现具备耐心的投资者往往在正确追损的路上更加游刃有余。他们总是可以保持较为平和的心态&#xff0c;不急不躁地分析原因并通过自身掌握的安全应对措施来进行交易。富格林在以下分享…...

前端vue2迁移至uni-app

1.确定文件存放位置 components: 继续沿用 pages: views内容移动到pages static: assets内容移动到static uni_modules: uni-app的插件存放位置 迁移前 src├─assets│ └─less├─components│ ├─common│ │ ├─CommentPart│ │ └─MessDetail│ ├─home│…...

网赌网站怎么做的/灰色关键词怎么做排名

函数malloc()和calloc()都可以用来动态分配内存空间,但两者稍有区别。malloc()函数有一个参数,即要分配的内存空间的大小: void *malloc(size_t size); calloc()函数有两个参数,分别为元素的数目和每个元素的大小,这两个参数的乘积就是要分配的内存空间的大小。 void *calloc(s…...

网站建设流程是这样的 里面有很/免费的api接口网站

解决方法如下&#xff1a; &#xff11;. 单击“窗口”菜单下面的“首选项”&#xff1a; &#xff12;. 选择“Java ”选项下的“编译器”选项&#xff1a; &#xff13;.在“编译器一致性级别”右边的下拉菜单里选择“6.0”&#xff1a; &#xff14;.单击“确定”按钮后在…...

ppt模板下载免费版幼儿园/seopc流量排行榜企业

问题&#xff1a;打开phpmyadmin显示高级功能尚未完全设置部分功能未激活&#xff0c;应该如何解决&#xff1f; 总共三步可以搞定 1、导入相关文件到数据库 2、更改配置文件config.inc.php 3、给于root用户相关权限 详细过程如下&#xff1a; 先找到 phpMyAdmin所在目录&a…...

如何建手机销售网站/搜索引擎优化心得体会

杨宇晴新时代&#xff0c;万象更新。为培养德智体美劳全面发展的社会主义建设者和接班人&#xff0c;教师也得踏上新征程。那么&#xff0c;如何做一名新时代的好老师呢&#xff1f;一、 博爱铸师魂(一)关心爱护学生1.细心的关爱&#xff0c;可以滋润孩子幼小的心田。还记得&am…...

如何给网站2做推广/第三方营销平台有哪些

目录(文件夹)相关的操作 1》mkdir &#xff08;make directories&#xff09;创建目录 语法&#xff1a;mkdir -p 目录名称 -p&#xff1a;当父母录不存在的情况下&#xff0c;同时创建父目录 2》rmdir &#xff08;remove empty directories&#xff09;删除目录 语法&a…...

wordpress 自定义登录/发布信息的免费平台

原因&#xff1a; 因为函数模板可以实现类型的自推&#xff0c;不需要类型参数列表。 类模板&#xff0c;必须指定模板的参数列表 因此使用函数模板将类模板对象的产生进行封装就可以避免类模板中指定模板的参数列表 类模板:insert_iterator(插入型迭代器&#xff09; inse…...