C++20 高级编程

文章目录

  • 前言
  • 前奏
    • lambda
    • 浅谈std::ref的实现
    • 浅谈is_same
    • 浅谈std::function的实现
    • std::visit 与 std::variant 与运行时多态
    • SFINAE
    • 类型内省
    • 标签分发 (tag dispatching)
    • 编译时多态
      • 奇异递归模板模式 (Curiously Recurring Template Pattern,CRTP)
    • 三路比较操作符 (飞船操作符) <=>
    • enable_shared_from_this 模板类
  • 概念约束
    • 定义概念
    • requires 表达式
  • 模板元编程
  • constexpr 元编程
  • Ranges 标准库
  • 协程
  • 软件设计六大原则 SOLID
  • To be continue....

前言

  • C++20 是C++在C++11 之后最大的一次语言变革, 其中引入了大量具有革命性的新特性.
  • 本节包含了C++20中相当重要的四大特性: 概念约束, ranges(范围)标准库, 协程以及模块
    • 概念约束:
             是一个编译期谓词, 它根据程序员定义的接口规范对类型,常量等进行编译时检查,以便在泛型编程中为使用者提供更好的可读性与错误信息.
    • ranges标准库:
             对现有的标准库进行了补充,它以函数式编程范式进行编程, 将计算任务分解成一系列灵活的原子操作, 使得代码的正确性更容易推理.
    • 协程:
             是一种可挂起, 可恢复的通用函数, 它的切换开销是纳秒级的, 相对其他方案而言占用的资源极低, 并且可以非侵入式地为已有库扩展协程接口, 它常常用于并发编程, 生成器, 流处理, 异常处理等.
    • 模块:
             解决了传统的头文件编译模型的痛点: 依赖顺序导致头文件难以组合, 重复解析, 符号覆盖等问题, 从语言层面为程序员提供了模块化的手段.
  • 涉及了一些元编程的概念

前奏

lambda

  • lambda其实是由编译器生成的一个匿名类
  • 如果lambda包含在捕获列表内, 那么捕获将在对应的匿名类中生成成员变量与构造函数来存储捕获;
  • 对于无捕获的lambda而言, 其生成的匿名类中拥有一个非虚的函数指针类型转换操作符, 能够将lambda转换成函数指针. 这个不难理解, 因为无状态的 lambda 表达式可以赋给无状态的函数指针;
  • 在C++20 中泛型 lambda 也支持以模板参数形式提供, 这样就能保证两个形参类型一致.
    auto add=[]<typename T>(T a,T b){return a+b;};
    cout<<add(1,2)<<endl;
  • 模板函数只有实例化之后才能传递, 而泛型 lambda 是一个对象,可以按值传递, 在调用时根据实际传参进行实例化 模板函数 operator(),从而延迟了实例化的时机, 大大提高了灵活性. 标准库的一些算法通常要求对函数对象进行组合, 此时泛型 lambda 将能通过编译, 而模板函数不行.

浅谈std::ref的实现

  • 就是说构造一个新的对象(reference_wrapper是一个类模板)并在内部保存原来传入的变量的地址 _f 和类型 type
  • 重载类型转换为原来变量的引用类型, 有了_f 和 type, 这样可以在需要的时候自动将reference_wrapper转换为原来传入的变量实体的引用;
  • reference_wrapper 她本身可能会被 decay 但内部存储的 _f 值始终不会变, 始终可以在需要的时候自动转换为 type&
  • 我们可以自己简单实现一下:
    template<typename T>
    class my_ref{
    public:T * _f;explicit  my_ref(T& var): _f(addressof(var)){};operator T& () { return *_f; };
    //如果传入的是一个可调用对象template<class... Args>auto&& operator()(Args&& ...args){return (*_f)(args...);}
    };
    
  • 参考: https://zhuanlan.zhihu.com/p/581739392

浅谈is_same

  • 这是C++11引入的, 其实很简单, 模板特化就行了. 让编译器决定. 考虑: 万一我要运行时才能确定类型呢?
    template<class T, class U> struct is_same : std::false_type {};
    //偏特化版本 待确认一个模板参数
    template<class T> struct is_same<T, T> : std::true_type {};
    

浅谈std::function的实现

  • 有点类似上面的std::ref
  • 主要是对函数参数列表类型的获取, 可以使用模板特化来达到目的 C++ Template -> [5] -> 自由函数与模板可变参数

std::visit 与 std::variant 与运行时多态

  • 用法参考:
    https://zhuanlan.zhihu.com/p/676918348
    https://zhuanlan.zhihu.com/p/670189611
  • std::variant 行为像是一个类型安全的联合体。它存储了一系列类型,并能够在运行时安全地处理这些类型之一。
  • 它保留足够的空间来存储其可能的任何类型,通常是这些类型中最大者的大小。此外,std::variant 还需要额外的存储空间来跟踪当前存储的类型。
  • 为了维护类型安全,std::variant 使用一个内部索引来标记当前激活的类型。当访问或修改 std::variant 的值时,它会检查这个索引,并确保操作符合当前激活的类型。
  • std::visit 的第一个参数传入一个可调用对象,后面传入的是可调用对象的参数(可以用variant)。 std::visit 的工作原理依赖于编程语言或编译器的内部机制,这些机制通常对程序员透明。其中一种可能的实现方式是使用 “vtable”(Virtual Table,虚函数表)。 总之, std::visit 会在运行时查找函数地址。每当创建一个 std::variant 对象的时候,就会产生一个与之关联的 vtable,同来存储这个 std::variant 中存储的相关信息。
  • visit 编译时静态绑定 (第二个参数中各类型对应的第一个参数中可调用函数版本绑定到类型对应的索引 (用vtable来存储映射关系) )
  • visit 运行时动态绑定 (检查variant中当前激活的类型的索引). 从而决定要调用的函数版本
  • subtype多态例子:
    #include <iostream>
    #include <memory>
    #include <cmath>
    namespace Subtype
    {struct Shape{virtual ~Shape() = default;virtual double getArea() const = 0;virtual double getPerimeter() const = 0;};struct Circle: Shape{Circle(double r): r_(r) {}double getArea() const override{return M_PI * r_ * r_;}double getPerimeter() const override{return 2 * M_PI * r_;}private:double r_;};struct Rectangle: Shape{Rectangle(double w, double h): w_(w), h_(h) {}double getArea() const override{return w_ * h_;}double getPerimeter() const override{return 2 * (w_ + h_);}private:double w_;double h_;};
    }
    using namespace Subtype;int main(int argc, char** argv) {std::unique_ptr<Shape> shape = std::make_unique<Circle>(2);// shape area: 12.5664 perimeter: 12.5664std::cout << "shape area: " << shape->getArea()<< " perimeter: " << shape->getPerimeter() << std::endl;shape = std::make_unique<Rectangle>(2, 3);// shape area: 6 perimeter: 10std::cout << "shape area: " << shape->getArea()<< " perimeter: " << shape->getPerimeter() << std::endl;return 0;
    }
    
  • ad-hoc多态例子:
    #include <variant>
    #include <cmath>
    #include <iostream>
    namespace Adhoc
    {struct Circle{double r;};// Circle的一系列操作double getArea(const Circle& c){return M_PI * c.r * c.r;}double getPerimeter(const Circle& c){return 2 * M_PI * c.r;};struct Rectangle{double w;double h;};// Rectangle的一系列操作double getArea(const Rectangle& r){return r.w * r.h;}double getPerimeter(const Rectangle& r){return 2 * (r.w + r.h);};// 通过加法类型定义一个统一的类型Shape,其拥有不同的形状,从而实现运行时多态using Shape = std::variant<Circle, Rectangle>;// 统一类型Shape的一系列多态行为double getArea(const Shape& s){return std::visit([](const auto & data){return getArea(data);}, s);}double getPerimeter(const Shape& s){return std::visit([](const auto & data){return getPerimeter(data);}, s);};
    }
    using namespace Adhoc;int main(int argc, char** argv) {Shape shape = Circle{2};// shape area: 12.5664 perimeter: 12.5664std::cout << "shape area: " << getArea(shape)<< " perimeter: " << getPerimeter(shape) << std::endl;shape = Rectangle{2, 3};// shape area: 6 perimeter: 10std::cout << "shape area: " << getArea(shape)<< " perimeter: " << getPerimeter(shape) << std::endl;return 0;
    }
    
  • image

    subtype 多态和 ad-hoc 多态的表现形式对比

    多态形式定义多态调用
    subtype 多态Abstract* objobj->method()
    ad-hoc 多态Abstract objmethod(obj)

SFINAE

  • substitution failure is not an error

  • 典型的用法是利用enable_if
    enable_if:

    template<bool, typename _Tp = void>struct enable_if{ };// Partial specialization for true.
    template<typename _Tp>struct enable_if<true, _Tp>//第二个参数默认为void{ typedef _Tp type; };
    

    如果是false, 那么第一个空类没有type成员, 这时直接使用其type将报错
    但在SFINAE决策上下文环境中, 这种情况可以做为一种决策条件:

    下面情况下, 编译器如果根据实参推断发现enable_if<false>没有定义成员类型type, 将导致替换失败, 将其从候选集中删除, 从而达到我们的目的

    template<class T, enable_if_t<is_integral_v<T>>* = nullptr>
    void test(T t)
    {cout << "is integral" << endl;};template<class T, enable_if_t<is_floating_point_v<T>>* = nullptr>
    void test(T t)
    {cout << "is floating_point" << endl;};
    
  • Tips: 函数重载的过程中只看函数的声明, 如果它被决策为最佳可行函数, 但模板函数体内发生了模板参数替换失败, 那么就会在实例化过程中产生编译错误, 而不是SFINAE

  • Tips: SFINAE机制在其他条件相同情况下, 总是会优先选择更明确更具体的类型版本, 这也是模板特化能起作用的原因

类型内省

  • 检查对象的类型或属性的一种能力
  • 在C++中类型萃取也可以视作内省
  • 就能实现把各种类型各种分离与组合, <type_trait> 实现了这些功能
  • 比如 C++ Template -> [5] -> 自由函数与模板可变参数 中也是利用了类型内省 类似的还有数组 template<class E,size_t N> someArr<E[N]>{…}

标签分发 (tag dispatching)

  • 除了 enable_if 之外的编译时多态手段还有标签分发, 这也是C++社区著名的管用手法;

  • 标签常常是一个空类, 没有别的什么, 只是当作一种类型. 好让编译器在SFINAE决策中把它作为参考依据, 匹配出最合适的版本

  • 辅助类 true_type 和 false_type 类型也可视作标签, 他们把 true 和 false 包装成两种类型 这样在编译时就能决策出最合适的版本

  • 示例:

    template<class T>bool numEqImpl(T l, T r, true_type)
    {cout << "is floating" << endl;return true;
    }
    template<class T>bool numEqImpl(T l, T r, false_type)
    {cout << "is not floating" << endl;return false;
    }
    template<class T> enable_if_t<is_arithmetic_v<T>, bool> numEq(T l, T r)
    {return numEqImpl(l, r, is_floating_point<T> {});//标签分发
    }
    

    怎么样, 是不是逐渐理解一切了?

    标准库中在<iterator>中定义了如下迭代器标签
    input_iterator_tag;
    forward_iterator_tag;
    bidirectional_iterator_tag;
    random_access_iterator_tag;
    并且提供了配套的元函数 iterator_traits, 输入迭代器,输出相关属性;
    例如类型成员::iterator_category 存储的是迭代器的种类标签,::value_type 是解引用后的类型, ::difference_type 为迭代器作差后的类型 ptrdiff_t(通常是long类型的别名)

    iterator_traits<myIterator>::iterator_category{}实际调用时这样开始分发 ,有了标签就不用浪费内存真的去构造一个实体iterator对象了(大部分标签实现为空类)
    看看advance函数 它也利用了迭代器标签来实现

    也可以尝试利用 if constexpr 来实现advance

编译时多态

除了前面多多少少讲的一些编译时多态手段, 还有下面这些

  • 奇异递归模板模式 (Curiously Recurring Template Pattern,CRTP)

    • 代码复用: 由于子类派生于模板基类, 因此可以复用基类的方法
    • 编译时多态: 由于基类是一个模板类, 能够获得传递进来的派生类, 进而可以调用派生类的方法, 达到多态的效果. 与运行时多态相比没有虚表的开销.
    • 通常是如下形式:
     template<class T> struct Base{/*...*/};//派生类struct Derived : Base<Derived>{/*...*/};
    

    看一个利用 CRTP 形成静态多态的例子:

     #include <iostream>using namespace std;template<typename Derived>struct Animal {void bark() { static_cast<Derived&>(*this).barkImpl(); }};class Cat: public Animal<Cat> {friend Animal;void barkImpl() { cout << "Miaowing!" << endl; }};class Dog: public Animal<Dog> {friend Animal;void barkImpl() { cout << "Bow wow!" << endl; }};template<typename T>void play(Animal<T>& animal) { animal.bark(); }int main(int argc, char** argv) {Cat cat; play(cat); // Miaowing!Dog dog; play(dog); // Bow wow!return 0;}
    

    从play的实现来看, 它接受一个统一的抽象接口类, 而调用的时候根据实际类型是Cat还是Dog进行多态调用, 由于类型是在编译时确定的, 所以多态调用也是在编译时绑定的. 为何不把play写成模板函数的形式? 这样无需模板基类Animal<T>也能实现静态多态.
           原因在于CRTP的模板基类可以起到接口类的作用, 它明确列出了该派生体系中所支持的一组操作, 换句话说Animal<T>其实是对它所接受的类型,所支持的方法的集合进行约束, 而普通的模板函数对模板参数没有约束, 需要使用高级技巧(如 enable_if)进行约束.
           C++演进的过程中考虑了这一点, 在 C++20 中提供 concept 特性对模板参数进行约束.

    Visitor Pattern (访问者模式)
    虽然现代C++使用 std::variant 与 std::visit 来代替访问者模式
    但先辈们想出来的这个复合了奇异递归模板模式的模式…其思想…高内聚低耦合总是现代C++所追求的…

    点击查看代码
    #include <iostream>
    #include <string>
    #include <memory>
    #include <vector>
    #include <variant>using namespace std;struct VideoFile;
    struct TextFile;struct Visitor {virtual void visit(VideoFile&) = 0;virtual void visit(TextFile&) = 0;virtual ~Visitor() = default;
    };struct Elem {virtual void accept(Visitor& visit) = 0;virtual ~Elem() = default;
    };template<typename T>
    struct dump;template<typename Derived>
    struct AutoDispatchElem: Elem {void accept(Visitor& visitor) override{ visitor.visit(static_cast<Derived&>(*this)); }
    };struct VideoFile: AutoDispatchElem<VideoFile> { /* ... */ };
    struct TextFile: AutoDispatchElem<TextFile> { /* ... */ };int main(int argc, char** argv) {std::vector<std::unique_ptr<Elem>> elems;elems.emplace_back(new VideoFile);elems.emplace_back(new TextFile);struct ConcreteVisitor: Visitor {void visit(VideoFile&) override{ cout << "process VideoFile ..." << endl; }void visit(TextFile&) override{ cout << "process TextFile ..." << endl; }} visitor;for (auto&& e: elems) e->accept(visitor);return 0;
    }

三路比较操作符 (飞船操作符) <=>

  • C++20 引入了三路比较操作符, 这样只需要实现一个直观的<=>操作符,就可以直接得到六个比较操作符<,==,!=,>,<=,>=
    image

    点击查看代码
    #include <iostream>
    #include <tuple>
    #include <cassert>using namespace std;namespace crtp {
    template<typename Derived>
    struct Comparable {friend bool operator==(const Derived& lhs, const Derived& rhs){ return lhs.tie() == rhs.tie(); }friend bool operator!=(const Derived& lhs, const Derived& rhs){ return !(lhs == rhs); }friend bool operator< (const Derived& lhs, const Derived& rhs){ return lhs.tie() < rhs.tie(); }friend bool operator> (const Derived& lhs, const Derived& rhs) { return rhs < lhs; }friend bool operator<=(const Derived& lhs, const Derived& rhs) { return !(lhs > rhs); }friend bool operator>=(const Derived& lhs, const Derived& rhs) { return !(lhs < rhs); }
    };struct Point: Comparable<Point> {Point(int x, int y): x(x), y(y) { }int x; int y;auto tie() const { return std::tie(x, y); }
    };
    }namespace classical {
    struct Point {int x; int y;friend bool operator==(const Point& lhs, const Point& rhs){ return std::tie(lhs.x, lhs.y) == std::tie(rhs.x, rhs.y); }friend bool operator<(const Point& lhs, const Point& rhs){ return std::tie(lhs.x, lhs.y) < std::tie(rhs.x, rhs.y); }friend bool operator!=(const Point& lhs, const Point& rhs) { return !(lhs == rhs); }friend bool operator> (const Point& lhs, const Point& rhs) { return rhs < lhs;     }friend bool operator<=(const Point& lhs, const Point& rhs) { return !(lhs > rhs);  }friend bool operator>=(const Point& lhs, const Point& rhs) { return !(lhs < rhs);  }
    };
    }namespace modern {
    struct Point { // C++20int x; int y;friend auto operator<=>(const Point& lhs, const Point& rhs) = default;
    };
    }#define PointTest() assert((Point{1,2} >= Point{1,0})); \assert((Point{1,2} <= Point{5,0})); \assert((Point{1,2} == Point{1,2})); \assert((Point{1,2} != Point{1,3})); \assert((Point{1,2} <  Point{1,3})); \assert((Point{3,2} >  Point{1,3}))int main(int argc, char** argv) {{using crtp::Point;PointTest();}{using classical::Point;PointTest();}{using modern::Point;PointTest();}return 0;
    }

enable_shared_from_this 模板类

  • 异步编程中存在一种场景, 需要在类中将该类的对象注册到某个回调类或函数中, 不能简单的将this传到回调类中, 很可能因为回调时该对象被释放而导致野指针访问, 也不能直接将this构造成shared_ptr, 因为无法直接通过裸指针获得引用计数信息, 强行构造也会导致对象内存被多次释放.
    struct Obj {void submit() {executor.submit([this] { this->onComplete(); });//内存风险}void onComplete() {/*...*/}
    };
    
    那么如果获得裸指针(this) 的引用计数信息呢? 标准库提供了enable_shared_from_this 模板基类 通过CRTP在父类中存储子类的指针信息与引用计数信息, 并提供 shared_from_this 和 weak_from_this 以获得智能指针.
    struct Obj : enable_shared_from_this<Obj> {void submit() {executor.submit([obj = weak_from_this()] {if (auto spObj = obj.lock()) spObj->onComplete();});}void onComplete() {/*...*/}
    };
    
    为在类内处理它本身引用计数逻辑提供了便利

概念约束

  • C++20 起对 concept 特性进行了标准化, 主流的编译器也提供了支持
    将一类数据类型和对它的一组操作所满足的公理集称为 concept: 不仅需要从语法上满足要求, 还需要从语义层面上满足.
  • concept 拥有强大的表达力, 程序员能够非常简单的定义一个概念, 也可以对概念库中已有的概念进行组合. 概念支持重载, 能够消除对变通方案(enable_if等技巧)的依赖, 大大降低了元编程的难度, 也简化了泛型编程.

定义概念

  • 是一个对类型约束的编译期谓词, 给定一个类型判断其能否满足语法和语义要求, 这对泛型编程而言极为重要.比如, 给定模板参数T, 对它的要求如下:

    1. 一种迭代器类型 Iterator<T>
    2. 一种数字类型 Number<T>
      Iterator 和 Number 就是概念
  • 基本定义语法:

    template<被约束的模板参数列表>
    concept 概念名 = 约束表达式;

    概念和模板 using 的别名很类似, 前者是对布尔表达式的别名, 而后者是对模板类型的别名.
    约束表达式 可以简单理解为 布尔常量表达式
    如果在定义概念时约束表达式类型不为 bool 类型, 将引发一个编译错误, 而不是返回 false

    //Atomic constraint must be of type 'bool' (found 'int')
    template <typename T> concept Foo=1;
    

    >.GCC编译器定义concept时不会报错, 在求值的时候才进行类型检查;

  • 在概念标准库<concepts>中可以看到:

    template<typename T>
    concept integral = is_integral_v<T>;
    template<typename T>
    concept floating_point = is_floating_point_v<T>;
    

    简单使用展示:

    static_assert(floating_point<float>);
    static_assert(! floating_point<int>);
    
  • 约束表达式中使用 && 与 ||
    合取(conjunction)与析取(disjunction)操作与逻辑表达式中的与或运算类似, 也是一个短路操作符.

    template <typename T>
    concept Foo = is_integral_v<typename T::type>||sizeof(T)>1;
    static_assert(Foo<double>);//ok,因为 sizeof(T)>1 满足(true)
    
  • 原子约束
    在依次对每个条件检查时, 首先检查表达式是否合法, 若不合法则该约束不满足,返回false 否则进一步对约束进行求值判断是否满足.
    对可变模板参数形成的约束表达式, 既不是约束合取也不是约束析取

    //要求每个模板参数都含有类型成员type, 否则整个表达式为false,
    //因为原子约束先检查整个表达式是否合法, 如果有一个不合法, 那么整个表达式就为 false 
    //在整个表达式全部合法的前提下, 才有意义谈|| && 才进入求值判断阶段
    // || 表示有一个参数符合就返回true,&& 表示所有参数符合才返回true
    template <typename... Ts>
    concept Foo1 = (is_integral_v<typename Ts::type>||...);
    

    如果你仔细看看这个表达式结构…oh my gosh! 这可不就是个折叠表达式么? C++ Template >> [ 5 ].模板的可变参数->Variadic Expressions

    非可变模板参数如何形成原子约束?

    template <typename T>
    concept Foo = bool(is_integral_v<typename T::type>||sizeof(T)>1);//原子约束
    
  • 中间层
    上面有关可变模板参数的 concept 想要表达 “至少一个模板参数存在类型成员 type 且类型成员为整型” 怎么办? 很简单, 创建一个约束中间层就行了
    concept 的返回值总可能合法

    template <typename T>//中间层
    concept Midal = is_integral_v<typename T::type>;
    template <typename... Ts>
    concept Foo1 = (Midal<Ts> ||...);
    
  • negation (逻辑否定)

    template<typename T>concept C1 = is_integral_v<typename T::type>;
    template<typename T>concept C2 =! is_integral_v<typename T::type>;
    //T存在type且为整数类型时 C3 为 false, 反之只要C1中有一条不满足则C1为false, C3这时就为true
    template<typename T>concept C3 =! C1<T>;struct Foo{using type = float ;};
    //static_assert(C1<int>);//C1 表达: 要求T存在关联类型type,且 关联类型为整数类型
    //static_assert(C1<Foo>);
    static_assert(C2<Foo>);//C2 表达: 要求T存在关联类型type,且 关联类型不为整数类型
    static_assert(C3<Foo>);//C3 表达: 要求T不存在关联类型type,或 关联类型不为整数类型
    static_assert(C3<int>);//其实是把C1当作一个整体来看,只看结果
    

    注意两者语义的细微差别

requires 表达式

模板元编程

constexpr 元编程

Ranges 标准库

协程

软件设计六大原则 SOLID

  • Single responsibility principle
  • Open Closed Principle
  • Liskov Substitution Principle (LSP)
  • Interface Segregation Principle
  • Dependence Inversion Principle
  • Law of Demeter
  • https://baike.baidu.com/starmap/view?nodeId=294b5d3c8cc7452ad5c9cdba&lemmaTitle=开闭原则&lemmaId=2828775&starMapFrom=lemma_starMap&fromModule=lemma_starMap

To be continue…

https://netcan.github.io/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/657794.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Django视图函数技巧,从入门到实战

文章目录 Django视图函数1.request对象的方法2.视图函数的常用的返回对象&#xff08;1&#xff09;response对象&#xff08;2&#xff09;JsonResponse对象&#xff08;3&#xff09;redirect() &#xff1a;给浏览器了一个30x的状态码 3.设置响应头和状态码&#xff08;1&am…

Apache Flink文件上传漏洞(CVE-2020-17518)漏洞代码分析

漏洞复现参考如下文章 Apache Flink文件上传漏洞&#xff08;CVE-2020-17518&#xff09;漏洞复现分析_文件上传漏洞复现cve-CSDN博客 分析代码的话&#xff0c;首先找到漏洞修复的邮件 漏洞详情&#xff0c;可以看到漏洞概要&#xff0c;影响的版本&#xff0c;漏洞描述以及…

【Linux笔记】文件描述符与重定向

一、Linux关于文件操作的一些系统调用 1、open和close 我们在C语言阶段已经学过很多文件操作的函数&#xff0c;今天我们要来看看操作系统中对于文件是怎么操作的。 1.1、open与close的用法 C语言的库函数中有很多关于文件操作的接口&#xff0c;包括fopen、fclose、fprint…

Docker容器引擎镜像创建

目录 一、镜像的创建 &#xff08;一&#xff09;基于现有镜像创建 1.启动一个镜像&#xff0c;在容器里做修改 2.将修改后的容器提交为新的镜像 &#xff08;二&#xff09;基于本地模板创建 &#xff08;三&#xff09;基于Dockerfile 创建 1.联合文件系统&#xff08…

五大架构之一:系统架构数据流风格

系统架构数据流风格详细介绍 系统架构数据流风格是一种软件体系结构风格&#xff0c;它强调了系统内部不同部分之间的数据流动。这种风格侧重于描述系统中的数据处理过程&#xff0c;以及数据是如何从一个组件传递到另一个组件的。以下是系统架构数据流风格的详细介绍&#xff…

redisTemplate.opsForValue()

redisTemplate ​在Spring Data Redis中&#xff0c;redisTemplate 是一个非常重要的组件&#xff0c;它为开发者提供了各种操作 Redis 的方法。对于 opsForValue() 方法&#xff0c;它是用来获取一个操作字符串值的操作对象。这意味着你可以使用它来执行各种字符串相关的操作…

[文本挖掘和知识发现] 02.命名实体识别之基于BiLSTM-CRF的威胁情报实体识别万字详解

作者于2023年8月新开专栏——《文本挖掘和知识发现》&#xff0c;主要结合Python、大数据分析和人工智能分享文本挖掘、知识图谱、知识发现、图书情报等内容。这些内容也是作者《文本挖掘和知识发现&#xff08;Python版&#xff09;》书籍的部分介绍&#xff0c;本书预计2024年…

前端面试题-网络请求-http请求方式-http状态码-url地址到浏览器渲染过程-跨域-请求测试工具-http和https

前端面试题-网络请求-http请求方式-http状态码-url地址到浏览器渲染过程-跨域-请求测试工具 http请求方式http的状态码有哪些&#xff1f;分别代表什么意思&#xff1f;从输入一个url地址到浏览器完成渲染的整个过程解决跨域的三种方式请求测试工具-postman的使用http和https h…

公司人才招聘工作开展难点分析

某国有资本运营公司位于北方某省级城市。在2019年&#xff0c;北方某市的当地政府提出组建专业化国有资本投资运营公司&#xff0c;大力开展专业化资本运营&#xff0c;推动国有资本进退留转市场出清和专业化重组的政策方针。为提高国有资产的管理运营能力&#xff0c;该市成立…

KAFKA高可用架构涉及常用功能整理

KAFKA高可用架构涉及常用功能整理 1. kafka的高可用系统架构和相关组件2. kafka的核心参数2.1 常规配置2.2 特殊优化配置 3. kafka常用命令3.1 常用基础命令3.1.1 创建topic3.1.2 获取集群的topic列表3.1.3 获取集群的topic详情3.1.4 删除集群的topic3.1.5 获取集群的消费组列表…

001集—shapefile(.shp)格式详解——arcgis

一、什么是shapefile Shapefile 是一种用于存储地理要素的几何位置和属性信息的非拓扑简单格式。shapefile 中的地理要素可通过点、线或面&#xff08;区域&#xff09;来表示。包含 shapefile 的工作空间还可以包含 dBASE 表&#xff0c;它们用于存储可连接到 shapefile 的要…

1 月 30 日算法练习-思维和贪心

文章目录 重复字符串翻硬币乘积最大 重复字符串 思路&#xff1a;判断是否能整除&#xff0c;如果不能整除直接退出&#xff0c;能整除每次从每组对应位置中找出出现最多的字母将其他值修改为它&#xff0c;所有修改次数即为答案。 #include<iostream> using namespace …

组件如何组织以提升维护性、扩展性

文章目录 一、提升组件的维护性和扩展性1.1、单一职责原则&#xff08;Single Responsibility Principle&#xff09;1.2、松耦合&#xff08;Loose Coupling&#xff09;1.3、高内聚&#xff08;High Cohesion&#xff09;1.4、模块化设计&#xff08;Modular Design&#xff…

从零开始复现GPT2(三):词表,Tokenizer和语料库的实现

源码地址&#xff1a;https://gitee.com/guojialiang2023/gpt2 GPT2 模型词表TokenizerTokenizer 类_normalize 方法_tokenize 方法_CHINESE_CHAR_RANGE 和 _PUNCTUATION_RANGE 数据集语料库TokenizedCorpus 类 模型 词表 定义了一个名为 Vocab 的类&#xff0c;用于处理和管理…

【项目日记(六)】第二层: 中心缓存的具体实现(下)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:项目日记-高并发内存池⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你做项目   &#x1f51d;&#x1f51d; 开发环境: Visual Studio 2022 项目日…

给准备从事软件开发工作的年轻人的13个建议

从事软件开发是一个不断学习和适应变化的过程。这里有一些针对刚入行或准备从事软件开发工作的年轻人的建议&#xff1a; 掌握基础知识&#xff1a;确保你有扎实的编程基础。了解至少一种编程语言的语法和核心概念&#xff0c;比如C语言、Python、Java或C#。同时&#xff0c;理…

Spring 中获取 Bean 对象的三种方式

目录 1、根据名称获取Bean 2、根据Bean类型获取Bean 3、根据 Bean 名称 Bean 类型来获取 Bean&#xff08;好的解决方法&#xff09; 假设 Bean 对象是 User&#xff0c;并存储到 Spring 中&#xff0c;注册到 xml 文件中 public class User {public String sayHi(){retur…

Meta开源Code Llama 70B,缩小与GPT-4之间的技术鸿沟

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

MIT6.5830 实验1

GoDB 介绍 实验中实现的数据库被称为GoDB&#xff0c;根据 readMe1 中的内容可知&#xff0c;GoDB 含有&#xff1a; Structures that represent fields, tuples, and tuple schemas; Methods that apply predicates and conditions to tuples; One or more access methods …

LeetCode 829. 连续整数求和

一开始我想的是质因数分解&#xff0c;然后项数 为奇数的好解决但是偶数弄不了 然后看题解发现了你直接写出通项公式&#xff1a; 假设首项是a&#xff0c;项数为k 则 (ak-1a)*k 2*n 看看k 的范围 2*a 2n/k 1-k>2 2*n/k >k1 2n>k*k 所以可以暴力枚举k sqrt…