C++11【下】

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:那个传说中的man的主页
🏠个人专栏:题目解析
🌎推荐文章:题目大解析(3)

在这里插入图片描述


目录

  • 👉🏻 新的类功能
    • 类成员变量初始化
    • 强制生成默认函数的关键字default
    • 禁止生成默认函数的关键字delete
    • 继承和多态中的final与override关键字
  • 👉🏻可变参数模板
    • empalce相关接口函数
  • 👉🏻Lambda表达式
    • 注意要点
    • 函数对象与lambda表达式
  • 👉🏻包装器
    • function包装器
    • function包装器与map的配合使用
  • 👉🏻bind函数
    • 绑定函数指针和参数
    • 绑定函数对象和参数
    • 绑定成员函数和对象指针
    • 绑定函数对象和引用参数

👉🏻 新的类功能

原来C++类中,有6个默认成员函数:

  1. 构造函数
  2. 析构函数
  3. 拷贝构造函数
  4. 拷贝赋值重载
  5. 取地址重载
  6. const 取地址重载
    默认成员函数就是我们不写编译器会生成一个默认的。

C++11 新增了两个:移动构造函数移动赋值运算符重载

🗣 针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下

  • 如果你没有自己实现移动构造函数,且没有实现析构函数拷贝构造拷贝赋值重载中的任
    意一个,即四没。那么编译器会自动生成一个默认移动构造默认生成的移动构造函数,对于内置类
    型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,
    如果实现了就调用移动构造,没有实现就调用拷贝构造
  • 如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中
    的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内
    置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋
    值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造
    完全类似)
  • 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值,如果需要,要自己写了老弟。

类成员变量初始化

C++11允许在类定义时给成员变量初始缺省值,默认生成构造函数会使用这些缺省值初始化

强制生成默认函数的关键字default

C++11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原
因这个函数没有默认生成。比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以
使用default关键字显示指定移动构造生成。

class Person
{
public:
 Person(const char* name = "", int age = 0)
 :_name(name)
 , _age(age)
 {}
 Person(const Person& p)
 :_name(p._name)
 ,_age(p._age)
 {}
 Person(Person&& p) = default;
private:
 bit::string _name;
 int _age;
};
int main()
{
 Person s1;
 Person s2 = s1;
 Person s3 = std::move(s1);
 return 0;
}

禁止生成默认函数的关键字delete

如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明补丁
已,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即
可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。

class Person
{
public:
 Person(const char* name = "", int age = 0)
 :_name(name)
 , _age(age)
 {}
 Person(const Person& p) = delete;
private:
 bit::string _name;
 int _age;
};
int main()
{
 Person s1;
 Person s2 = s1;
 Person s3 = std::move(s1);
 return 0;
}

继承和多态中的final与override关键字

这个在【C++】多态中,不再赘述

👉🏻可变参数模板

C++11引入了可变参数模板,它允许函数或类模板接受可变数量的参数。使用可变参数模板可以编写更加灵活和通用的代码。

下面就是一个基本可变参数的函数模板

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}

上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数
包”,它里面包含了0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,
只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特
点,也是最大的难点,即如何展开可变模版参数。由于语法不支持使用args[i]这样方式获取可变
参数,所以我们的用一些奇招来一一获取参数包的值

下面是一个示例,展示了用递归函数方式展开参数包进行使用可变参数模板

#include <iostream>

// 基本情况,递归终止条件
void print() {
    std::cout << std::endl;
}

// 可变参数模板,递归调用打印每个参数
template<typename T, typename... Args>
void print(const T& first, const Args&... args) {
    std::cout << first << " ";
    print(args...);  // 递归调用,传递剩余参数
}

int main() {
    print(1, 2, 3, "hello", 4.5);
    return 0;
}

在上面的示例中,我们定义了一个可变参数模板函数print,它可以接受任意数量的参数。基本情况是一个空函数print(),当没有参数时输出一个换行符。递归情况使用模板参数包(typename... Args)来接受可变数量的参数,并使用递归调用打印每个参数。在main函数中,我们调用print函数,传递了整数、字符串和浮点数作为参数,它们都被打印出来。

使用逗号表达式展开参数包

template <class T>
void PrintArg(T t)
{
 cout << t << " ";
}
//展开函数
template <class ...Args>
void ShowList(Args... args)
{
 int arr[] = { (PrintArg(args), 0)... };
 //int arr[] = { PrintArg(args)...};
 cout << endl;
}
int main()
{
 ShowList(1);
 ShowList(1, 'A');
 ShowList(1, 'A', std::string("sort"));
 return 0;
}

empalce相关接口函数

template <class... Args>
void emplace_back (Args&&... args);

首先我们看到的emplace系列的接口,支持模板的可变参数,并且万能引用。那么相对insert和
emplace系列接口的优势到底在哪里呢?

int main()
{
 std::list< std::pair<int, char> > mylist;
 // emplace_back支持可变参数,拿到构建pair对象的参数后自己去创建对象
 // 那么在这里我们可以看到除了用法上,和push_back没什么太大的区别
 mylist.emplace_back(10, 'a');
 mylist.emplace_back(20, 'b');
 mylist.emplace_back(make_pair(30, 'c'));
 mylist.push_back(make_pair(40, 'd'));
 mylist.push_back({ 50, 'e' });
 for (auto e : mylist)
 cout << e.first << ":" << e.second << endl;
 return 0;
}
int main()
{
// 下面我们试一下带有拷贝构造和移动构造的bit::string,再试试呢
// 我们会发现其实差别也不到,emplace_back是直接构造了,push_back
// 是先构造,再移动构造,其实也还好。
 std::list< std::pair<int, bit::string> > mylist;
 mylist.emplace_back(10, "sort");
 mylist.emplace_back(make_pair(20, "sort"));
 mylist.push_back(make_pair(30, "sort"));
 mylist.push_back({ 40, "sort"});
 return 0;
}

emplace_backpush_back都是向容器中添加元素的成员函数,但它们有一些重要的区别。

push_back函数会创建一个临时对象,并将其复制或移动到容器中。如果容器存储的是对象而不是指针,那么这将涉及到对象的复制或移动构造函数和析构函数的调用。这个额外的开销可能会导致效率降低,特别是在频繁插入元素的情况下。

相比之下,emplace_back函数直接在容器的末尾就地构造一个新的元素(直接构造),而不是先创建一个临时对象。这意味着我们可以避免创建临时对象、复制或移动和析构操作,从而提高性能。实际上,emplace_back通常比push_back更快。

在使用emplace_back时,我们需要注意以下几点:

  1. emplace_back参数应该与容器中元素的构造函数相匹配。

  2. 如果容器存储的是指针,则必须传递指向堆中分配的对象的指针。否则,在容器释放时可能会出现内存泄漏。

  3. 对于std::vectorstd::string等容器,emplace_backpush_back的语义是相同的。

总之,emplace_back是一个更加高效的添加元素的方法,可以避免创建临时对象并提高性能。

👉🏻Lambda表达式

C++11引入了lambda表达式,它是一种方便的方式来创建匿名函数。lambda表达式可以在需要函数对象的地方使用,并且可以捕获周围作用域中的变量。

一个基本的lambda表达式的语法如下:

[capture list](parameters)mutable -> return_type { 
    // 函数体
}

其中:

  • capture list:捕获列表,用于指定lambda表达式访问的外部变量。
  • parameters:参数列表,可选地指定输入参数。
  • return_type:返回类型,可选地指定返回值类型。
  • {}:函数体,包含实际的操作和代码逻辑。
  • mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性mutable关键字用于在lambda表达式中标记捕获的变量可以被修改。使用该修饰符时,参数列表不可省略(即使参数为空)。

下面是一个简单的示例,展示了lambda表达式的用法:

#include <iostream>

int main() {
    int x = 5;
    int y = 10;

    auto sum = [](int a, int b) -> int {
        return a + b;
    };

    auto result = sum(x, y);
    std::cout << "Sum: " << result << std::endl;

    return 0;
}

在上述示例中,我们定义了一个lambda表达式 sum,它接受两个整数参数并返回它们的和。然后,我们使用该lambda表达式计算变量 xy 的和,并将结果存储在变量 result 中。

lambda表达式还支持捕获外部变量。捕获列表可以指定要在lambda表达式内部使用的外部变量。可以通过值捕获([x])、引用捕获([&x])或混合捕获([x, &y])来捕获变量。

下面是一个使用捕获列表的示例:

#include <iostream>

int main() {
    int x = 5;
    int y = 10;

    auto sum = [x](int a, int b) -> int {
        return a + b + x;
    };

    auto result = sum(y, 15);
    std::cout << "Sum: " << result << std::endl;

    return 0;
}

在上述示例中,我们使用值捕获 [x] 来捕获变量 x,并在lambda表达式内部使用它计算结果。注意,在捕获列表中指定的变量将会被复制到lambda表达式的闭包中。

lambda表达式还可以省略参数列表和返回类型,让编译器自动推断。如果lambda表达式没有参数,则可以使用空括号 () 表示。如果返回类型可以被推断出来,则可以省略返回类型。

除了上述基本用法,lambda表达式还可以与标准库算法(例如std::for_eachstd::transform等)一起使用,以提供更强大的功能。

🍓捕获列表说明

  • [var]:表示值传递方式捕捉变量var
  • [=]:表示值传递方式捕获所有父作用域中的变量(包括this)
  • [&var]:表示引用传递捕捉变量var,传引用不用multable,可以修改
  • [&]:表示引用传递捕捉所有父作用域中的变量(包括this)
  • [this]:表示值传递方式捕捉当前的this指针

注意要点

  • 父作用域指包含lambda函数的语句块

  • 语法上捕捉列表可由多个捕捉项组成,并以逗号分割。
    比如:[=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量
    [&,a, this]:值传递方式捕捉变量a和this,引用方式捕捉其他变量

  • 捕捉列表不允许变量重复传递,否则就会导致编译错误。
    比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复

  • 在块作用域以外的lambda函数捕捉列表必须为空。

  • 在块作用域中的lambda函数仅能捕捉父作用域中局部变量,捕捉任何非此作用域或者
    非局部变量都
    会导致编译报错。

  • lambda表达式之间不能相互赋值,即使看起来类型相同

函数对象与lambda表达式

函数对象,又称为仿函数,即可以想函数一样使用的对象,就是在类中重载了operator()运算符的
类对象。

class Rate
{
public:
 Rate(double rate): _rate(rate)
 {}
 double operator()(double money, int year)
 { return money * _rate * year;}
private:
 double _rate;
};
int main()
{
// 函数对象
 double rate = 0.49;
 Rate r1(rate);
 r1(10000, 2);
// lamber
 auto r2 = [=](double monty, int year)->double{return monty*rate*year; 
};
 r2(10000, 2);
 return 0;
 }

从使用方式上来看,函数对象与lambda表达式完全一样。
函数对象将rate作为其成员变量,在定义对象时给出初始值即可,lambda表达式通过捕获列表可
以直接将该变量捕获到。
在这里插入图片描述
实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如
果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。

👉🏻包装器

C++11引入了一系列包装器(Wrappers),用于在编程中提供更多的灵活性和功能。这些包装器是标准库中的模板类,用于封装和操作其他类型的对象。

以下是C++11中的几个常见的包装器:

  1. std::pairstd::pair是一个模板类,用于存储两个不同类型的值。它提供了方便的方式来将两个值组合在一起,并且可以通过firstsecond成员访问其中的值。

  2. std::tuplestd::tuple是一个模板类,用于存储多个不同类型的值。与std::pair类似,std::tuple允许将多个值组合在一起,并且可以使用std::get函数按索引或类型访问其中的值。

  3. std::functionstd::function是一个通用的函数包装器,可以存储可调用对象(如函数、函数指针、lambda表达式等)。它提供了一种机制来传递和存储不同类型的可调用对象,并且可以像普通函数一样进行调用。

  4. std::reference_wrapperstd::reference_wrapper是一个模板类,用于包装对另一个对象的引用。它提供了一种非拥有(non-owning)引用的方式,允许将引用作为值进行传递和存储,而不是像指针一样对对象进行拥有。

这些包装器提供了更高层次的抽象,使得在编程中可以更灵活地处理不同类型的数据和对象。它们有助于提高代码的可读性、可维护性和重用性,并且在许多情况下可以简化编程任务。

function包装器

std::function是一个通用的函数包装器,它可以存储可调用对象(如函数名(函数指针)函数对象lambda表达式类的成员函数等)。使用std::function可以将任何可调用对象视为具有相同签名的函数,并使用相同的方法进行调用。

🍔以下有案例使用

#include <functional>
int f(int a, int b)
{
 return a + b;
}
struct Functor
{
public:
 int operator() (int a, int b)
 {
 return a + b;
 }
};
class Plus
{
public:
 static int plusi(int a, int b)
 {
 return a + b;
 }
 double plusd(double a, double b)
 {
 return a + b;
 }
};
int main()
{
 // 函数名(函数指针)
 std::function<int(int, int)> func1 = f;
 cout << func1(1, 2) << endl;
 // 函数对象
 std::function<int(int, int)> func2 = Functor();
 cout << func2(1, 2) << endl;
 // lamber表达式
 std::function<int(int, int)> func3 = [](const int a, const int b) 
{return a + b; };
 cout << func3(1, 2) << endl;
 
 // 类的成员函数
 std::function<int(int, int)> func4 = &Plus::plusi;
 cout << func4(1, 2) << endl;
 std::function<double(Plus, double, double)> func5 = &Plus::plusd;
 cout << func5(Plus(), 1.1, 2.2) << endl;
 return 0;
}

类的成员函数有点特殊,要&取地址和给出类域。

function包装器与map的配合使用

std::functionstd::map可以很好地配合使用,以实现基于字符串的事件处理程序或回调函数的映射。具体来说,可以将不同的字符串映射到不同的std::function对象上,然后根据字符串查找相应的函数并调用它。

例如,假设需要实现一个简单的命令行工具,用户可以输入不同的命令,然后程序会执行相应的操作。可以使用std::map将不同的命令字符串映射到相应的std::function对象上,然后在用户输入命令时查找相应的函数并调用它。

以下是一个示例代码:

#include <iostream>
#include <map>
#include <functional>

void print_help() {
    std::cout << "Usage: command [args...]\n"
              << "Available commands:\n"
              << "  help - Print this help message\n"
              << "  echo - Print the input arguments\n";
}

void echo(const std::vector<std::string>& args) {
    for (const auto& arg : args) {
        std::cout << arg << " ";
    }
    std::cout << std::endl;
}

int main() {
    std::map<std::string, std::function<void(const std::vector<std::string>&)>> commands{
        {"help", [](const std::vector<std::string>& args){ print_help(); }},
        {"echo", echo}
    };

    std::string command;
    while (true) {
        std::cout << "> ";
        std::getline(std::cin, command);

        std::istringstream iss(command);
        std::string name;
        iss >> name;

        auto it = commands.find(name);
        if (it != commands.end()) {
            std::vector<std::string> args(std::istream_iterator<std::string>{iss},
                                          std::istream_iterator<std::string>{});
            it->second(args);
        }
        else {
            std::cout << "Unknown command: " << name << std::endl;
        }
    }

    return 0;
}

在上面的代码中,我们定义了两个命令函数print_helpecho,分别用于打印帮助信息和输出输入的参数。然后,我们将这两个函数分别存储在std::function对象中,并使用std::map将命令字符串映射到相应的函数上。

main函数中,我们使用一个无限循环来等待用户输入命令。每次输入命令后,我们根据空格拆分输入字符串,并将第一个单词作为命令名称。然后,我们在commands中查找与命令名称匹配的函数,并将剩余的参数传递给它进行处理。

需要注意的是,我们在std::map中使用了std::function对象作为值类型,并指定了函数签名为void(const std::vector<std::string>&),以便与命令处理函数进行匹配。此外,我们还使用了C++11中的初始化列表语法来方便地初始化commands对象。

👉🏻bind函数

std::bind是一个函数,它可以把一个可调用对象和一些参数绑定在一起,返回一个新的可调用对象。这个新的可调用对象可以像原来的可调用对象一样调用,但是其参数已经固定了。

std::bind的语法比较复杂,需要注意以下几点:

  1. 第一个参数是要绑定的可调用对象,可以是函数指针、函数对象、成员函数指针等。
  2. 之后的参数是要绑定的参数,可以是任意类型的值或引用(包括占位符_1_2等)。
  3. 返回值是一个新的可调用对象,其参数个数和类型与原来的可调用对象不同,但可以通过占位符进行传递。

下面是一些常见的用法

绑定函数指针和参数

#include <functional>
#include <iostream>

void foo(int a, int b) {
    std::cout << a << " + " << b << " = " << (a + b) << std::endl;
}

int main() {
    auto f = std::bind(foo, 2, 3);
    f(); // 输出 2 + 3 = 5

    return 0;
}

在上面的代码中,我们使用std::bind将函数foo和参数2、3绑定在一起,生成一个新的可调用对象f。然后,我们调用f()就会执行foo(2, 3)

绑定函数对象和参数

#include <functional>
#include <iostream>

struct Bar {
    void operator()(int a, int b) const {
        std::cout << a << " - " << b << " = " << (a - b) << std::endl;
    }
};

int main() {
    Bar bar;
    auto f = std::bind(bar, 5, std::placeholders::_1);
    f(3); // 输出 5 - 3 = 2

    return 0;
}

在上面的代码中,我们定义了一个函数对象Bar,并重载了其括号运算符。然后,我们使用std::bind将函数对象bar和参数5、占位符_1绑定在一起,生成一个新的可调用对象f。在调用f(3)时,实参3会替换掉占位符_1,最终会执行bar(5, 3)

绑定成员函数和对象指针

#include <functional>
#include <iostream>

struct Baz {
    void hello(int n) const {
        std::cout << "hello " << n << std::endl;
    }
};

int main() {
    Baz baz;
    auto f = std::bind(&Baz::hello, &baz, std::placeholders::_1);
    f(42); // 输出 hello 42

    return 0;
}

在上面的代码中,我们定义了一个类Baz,其中包含一个成员函数hello。然后,我们创建一个Baz对象baz,并使用std::bind将其成员函数hello和对象指针&baz、占位符_1绑定在一起,生成一个新的可调用对象f。在调用f(42)时,实参42会替换掉占位符_1,最终会执行baz.hello(42)

需要注意的是,在绑定成员函数时,需要使用成员函数指针,同时还需要传入一个对象指针作为第一个参数(可以使用&取地址符)。

绑定函数对象和引用参数

#include <functional>
#include <iostream>

struct Foo {
    void operator()(int& n) const {
        n += 10;
    }
};

int main() {
    int x = 5;
    Foo foo;
    auto f = std::bind(foo, std::ref(x));
    f(); // 修改 x 的值为 15
    std::cout << x << std::endl;

    return 0;
}

在上面的代码中,我们定义了一个函数对象Foo,其中包含一个括号运算符,接受一个整数引用,并将其加上10。然后,我们创建一个整数变量x,并使用std::bind将函数对象foo和整数引用x绑定在一起,生成一个新的可调用对象f。在调用f()时,x的值会被修改为15。

需要注意的是,在绑定引用参数时,需要使用std::ref函数将其转换为引用。如果直接绑定一个值,那么修改的只是副本,而不是原来的变量。


如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长

在这里插入图片描述
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于算能的AI边缘计算盒子,内置强悍TPU | 32TOPS INT8算力

边缘计算盒子 内置强悍TPU | 32TOPS INT8算力 ● 支持浮点运算的TPU平台盒子&#xff0c;支持32TOPSINT8&#xff0c;16TFLOPSFP16&#xff0c;2TFLOPSFP32高算力 ● 单芯片最高支持32路H.264 & H.265的实时解码能力 ● 支持国产算法框架Paddle飞桨&#xff0c;适配Caf…

Linux--初识和基本的指令(3)

目录 1.前言 1.指令 1.1 cat指令 1.2 echo指令 1.3 more 指令 1.4 less指令 1.5 什么时候使用less和more 1.6 head指令 1.7 tail指令 1.8 wc指令 1.9 与时间相关的指令 1.9.1 date指令 1.9.2 cal指令 1.10 16.find指令&#xff1a;&#xff08;灰常重要&#x…

Python学习路线 - Python语言基础入门 - 准备工作

Python学习路线 - Python语言基础入门 - 准备工作 初识PythonPython的优点 什么是编程语言Python环境安装Windows系统Python安装Python验证 MacOS系统Linux系统 第一个Python程序常见问题 Python解释器Python解释器概念Python解释器存放位置Python解释器运行".py"文件…

Android File Transfer for Mac:畅享强大的安卓文件传输工具

作为一款功能强大的安卓文件传输工具&#xff0c;Android File Transfer for Mac&#xff08;以下简称AFT&#xff09;为Mac用户提供了便捷快速的安卓设备文件管理体验。无论是传输照片、音乐、视频还是文档&#xff0c;AFT都能满足你的需求&#xff0c;让你的文件传输更加高效…

ffmpeg 查看本地摄像头和麦克风设备名称

命令 ffmpeg -list_devices true -f dshow -i dummy 2

【代码】基于多能互补的电热联供型微网优化运行(完美复现)matlab+yalmip+cplex

MATLAB代码&#xff1a;基于多能互补的电热联供型微网优化运行&#xff0c;电热需求响应参与的综合能源微网协同优化调度&#xff0c;引入电热储能解耦电热联系&#xff0c;破解以热定电 关键词&#xff1a;热电联供型微网&#xff1b;热电解耦&#xff1b;综合能源需求响应&a…

读书笔记:《Effective Modern C++(C++14)》

Effective Modern C&#xff08;C14&#xff09; GitHub - CnTransGroup/EffectiveModernCppChinese: 《Effective Modern C》- 完成翻译 Deducing Types 模版类型推导&#xff1a; 引用&#xff0c;const&#xff0c;volatile被忽略数组名和函数名退化为指针通用引用&#…

关于安科瑞预付费系统在转供电情况下的应用-安科瑞 蒋静

1 前言 国家从2018年开始对转供电加价开展规范清理以来&#xff0c;已经出来了一系列政策&#xff0c;不仅包括专门针对转供电问题的政策&#xff0c;18-20年间还在每次降电价政策中突出强调了转供电主体不得截留降价红利的要求。从具体内容看&#xff0c;各地政策都鼓励一户一…

rtmp 协议详解

1. handshake 1.1 概述 rtmp 连接从握手开始。它包含三个固定大小的块。客户端发送的三个块命名为 C0,C1,C2&#xff1b;服务端发送的三个块命名为 S0,S1,S2。 握手序列&#xff1a; 客户端通过发送 C0 和 C1 消息来启动握手过程。客户端必须接收到 S1 消息&#xff0c;然后…

05-建造者模式-C语言实现

UML类图&#xff1a; 代码实现&#xff1a; #include <stdio.h> #include <stdlib.h>// 产品类 typedef struct {char* part1;char* part2;char* part3; } Product;// 抽象建造者类 typedef struct {void (*buildPart1)(void*, const char*);void (*buildPart2)(v…

8.4 Windows驱动开发:文件微过滤驱动入门

MiniFilter 微过滤驱动是相对于SFilter传统过滤驱动而言的&#xff0c;传统文件过滤驱动相对来说较为复杂&#xff0c;且接口不清晰并不符合快速开发的需求&#xff0c;为了解决复杂的开发问题&#xff0c;微过滤驱动就此诞生&#xff0c;微过滤驱动在编写时更简单&#xff0c;…

固定Microsoft Edge浏览器的位置设置,避免自动回调至中国

问题描述 在使用Copilot等功能时&#xff0c;需要将Microsoft Edge浏览器的位置设置为国外。但每次重新打开浏览器后&#xff0c;位置设置又自动回调至中国&#xff0c;导致每次均需要手动调整。 原因分析 这个问题的出现是因为每次启动Microsoft Edge时&#xff0c;默认打开…

VT-MRPA1-151-1X/V0/0控制2FRE16模块式模拟放大器

适用于控制带有电气位置反馈的直动式比例减压阀&#xff08;DBETR- 1X 类型&#xff09;或带有电气位置反馈的比例流量控制阀&#xff08;2FRE... 类型&#xff09;&#xff1b;控制值输入 1 0 V&#xff08;差动输入&#xff09;&#xff1b; 可分别调节“上/下”斜坡时间的斜…

RCE绕过

1.[SCTF 2021]rceme 总结下获取disabled_funciton的方式 1.phpinfo() 2.var_dump(ini_get(“disable_functions”)); 3.var_dump(get_cfg_var(“disable_functions”)); 其他的 var_dump(get_cfg_var(“open_basedir”)); var_dump(ini_get_all()); <?php if(isset($_POS…

MMdetection3.0 问题:DETR验证集上AP值全为0.0000

MMdetection3.0 问题&#xff1a;DETR验证集上AP值全为0.0000 条件&#xff1a; 1、选择的是NWPU-VHR-10数据集&#xff0c;其中训练集455张&#xff0c;验证、测试相同均为195张。 2、在Faster-rcnn、YOLOv3模型上均能训练成功&#xff0c;但是在DETR训练时&#xff0c;出现验…

分享89个节日PPT,总有一款适合您

分享89个节日PPT&#xff0c;总有一款适合您 89个节日PPT下载链接&#xff1a;https://pan.baidu.com/s/1j6Yj-7UCcUyV4V_S_eGjpQ?pwd6666 提取码&#xff1a;6666 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理更不易…

【计算机毕业设计】java网上服装商城销售系统springboot+jsp

目前互联网上的网上销售系统每天以惊人的速度增加&#xff0c;网上购物越来越成为现代年轻人的首选&#xff0c;足不出门便能选购好自己称心如意的商品。基于Java术及MySQL数据库设计与实现一个网上服装销售系统&#xff0c;要求实现用户管理、服装管理、新闻公告管理、留言管理…

vue项目报错及解决npm run build:prod打包错误

vue项目报错及解决npm run build:prod打包错误 执行dev环境时加载失败了该变量&#xff0c;在package.json文件中 删掉 解决方法&#xff1a; 打包成功&#xff1a;

03-抽象工厂模式-C语言实现

说明&#xff1a; 均由 chatgpt生成&#xff0c;实例可以看出无法运行&#xff0c;仅供参考~ 抽象工厂模式&#xff1a; 代码实现&#xff1a; #include <stdio.h>// 定义抽象产品接口 typedef struct {void (*operation)(); } AbstractProductA;typedef struct {voi…

linux设置权限_setfacl_getfacl

3.2 设置权限ACL&#xff08;access control list&#xff09; 假设&#xff1a;/data所有者与所属组均为root&#xff0c;在不改变所有者的前提下&#xff0c;要求用户tom对该目录有完全访问权限&#xff08;rwx&#xff09;。只能考虑&#xff1a; 方法一&#xff1a;给/dat…