【高级程序设计语言C++】C++11

news2024/12/25 9:08:10

  • 1. lambda表达式
  • 2. 移动构造函数和移动赋值运算符重载
  • 3. default关键字
  • 4. delete关键字
  • 5. 可变参数模板
  • 6. push_back和emplace_back的区别
    • 6.1. emplace_back的优势
  • 7. 包装器
    • 7.1. 包装器的语法格式

1. lambda表达式

在C++中,lambda表达式是一种用于创建匿名函数的语法。它允许我们在需要的地方定义一个临时的函数,而不需要显式地声明一个函数。

想象一下,你是一个小朋友,你喜欢画画。有一天,你的奶奶给你一个任务,让你根据不同的主题画不同的画。你不想每次都写一个新的函数来完成这个任务,因为这样会很麻烦。你希望能够在需要的时候,快速地定义一个临时的函数来完成任务。在C++中,lambda表达式就是用来解决这个问题的。

lambda表达式的语法如下:

[capture list] (parameter list) -> return type { function body }
  • capture list:用于捕获外部变量。可以是空的[],表示不捕获任何外部变量;也可以是[&],表示以引用方式捕获所有外部变量;还可以是[=],表示以值方式捕获所有外部变量。还可以使用具体的外部变量名来指定捕获方式,比如[x]表示以值方式捕获变量x。

在C++中,[]符号用于指定lambda表达式的捕获列表(Capture List)。捕获列表用于在lambda表达式中访问外部变量。

捕获列表有以下几种形式:

  1. []:不捕获任何外部变量。
  2. [变量名]:捕获指定的外部变量,可以是多个变量,用逗号分隔。
  3. [=]:以值的方式捕获所有外部变量,即复制一份外部变量的值到lambda表达式中。在lambda表达式中修改捕获的变量不会影响外部变量。
  4. [&]:以引用的方式捕获所有外部变量,即直接引用外部变量。在lambda表达式中修改捕获的变量会影响外部变量。
  5. [变量名1, 变量名2, …]:以值的方式捕获指定的外部变量,可以是多个变量,用逗号分隔。
  6. [&变量名1, &变量名2, …]:以引用的方式捕获指定的外部变量,可以是多个变量,用逗号分隔。

下面是一个示例代码,演示了lambda表达式中捕获列表的使用:

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

    // 捕获x和y,以值的方式
    auto lambda1 = [=]() {
        std::cout << "x + y = " << x + y << std::endl;
        std::cout << "x = " << x << ", y = " << y << std::endl; // 输出: x = 10, y = 20

    };

    // 捕获x和y,以引用的方式
    auto lambda2 = [&]() {
        x++;
        y++;
        std::cout << "x + y = " << x + y << std::endl;
        std::cout << "x = " << x << ", y = " << y << std::endl;// 输出: x = 11, y = 21
    };

    lambda1(); // 输出: x + y = 30
    lambda2(); // 输出: x = 11, y = 21

    return 0;
}

在这个例子中,我们定义了两个lambda表达式lambda1和lambda2,它们分别捕获了外部变量x和y。

lambda1以值的方式捕获了x和y,在lambda表达式中只能读取这些变量的值,不能修改它们。

lambda2以引用的方式捕获了x和y,在lambda表达式中可以读取和修改这些变量的值,对x和y的修改会反映到外部变量。

通过运行这段代码,我们可以看到lambda表达式根据捕获列表的不同,对外部变量的访问和修改也不同。

总之,[]符号用于指定lambda表达式的捕获列表,通过捕获外部变量,lambda表达式可以访问和修改这些变量的值。不同的捕获方式会导致不同的访问和修改行为。

  • parameter list:用于指定lambda函数的参数列表。和普通函数的参数列表一样,可以包含参数名和类型。

  • return type:用于指定lambda函数的返回类型。可以省略,编译器会自动推导返回类型。

  • function body:用于定义lambda函数的具体实现。

下面是一个lambda表达式的例子:

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

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

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

    return 0;
}

输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这个例子中,我们定义了一个lambda表达式来计算两个整数的和。在这个例子中,参数列表是(int a, int b),函数体是return a + b;。

我们使用关键字auto来声明一个变量sum,并将lambda表达式赋值给它。这样,sum就成为了一个可以调用的函数对象,可以像普通函数一样使用。

在main函数中,我们调用了sum函数,并将结果赋给变量result。然后,我们使用std::cout打印出了结果。

lambda表达式的优点是它可以在需要的地方快速定义一个临时的函数,避免了显式地声明一个函数。它可以捕获外部变量,让我们在函数体内部使用外部变量。它还可以作为参数传递给其他函数,使代码更加灵活和可读。

总之,lambda表达式是C++中的一种匿名函数,可以在需要的地方快速定义一个临时的函数。它的语法简洁,可以捕获外部变量,并可以作为参数传递给其他函数。

2. 移动构造函数和移动赋值运算符重载

当我们在C++中使用对象时,有时候需要进行对象的拷贝或赋值操作。通常情况下,拷贝构造函数和拷贝赋值运算符会被调用来完成这些操作。但是,在某些情况下,拷贝操作可能会比较耗时,特别是当对象的数据较大时。为了提高效率,C++引入了移动构造函数和移动赋值运算符。移动构造函数和移动赋值运算符允许将一个对象的资源(比如内存)从一个对象转移到另一个对象,而不是进行深拷贝。这样可以避免不必要的内存分配和释放,提高程序的性能。

针对这两个函数,需要注意的点是:

  1. 如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。

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

  3. 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。

假设奶奶有一本书,她想把这本书给小学生看。如果奶奶使用拷贝操作,她需要先复制这本书的内容,然后再给小学生。这样做会浪费时间和资源。

但是,如果奶奶使用移动操作,她只需要将这本书直接给小学生,而不需要复制内容。这样就更加高效。

class Book 
{
public:
    Book() 
    {
        std::cout << "默认构造函数" << std::endl;
        data = new int[1000000]; // 假设data是一个大型的数据结构
    }

    Book(const Book& other) 
    {
        std::cout << "拷贝构造函数" << std::endl;
        data = new int[1000000];
        std::copy(other.data, other.data + 1000000, data);
    }

    Book(Book&& other) 
    {
        std::cout << "移动构造函数" << std::endl;
        data = other.data;
        other.data = nullptr;
    }

    Book& operator=(const Book& other) 
    {
        std::cout << "拷贝赋值运算符" << std::endl;
        if (this != &other) {
            delete[] data;
            data = new int[1000000];
            std::copy(other.data, other.data + 1000000, data);
        }
        return *this;
    }

    Book& operator=(Book&& other) 
    {
        std::cout << "移动赋值运算符" << std::endl;
        if (this != &other) {
            delete[] data;
            data = other.data;
            other.data = nullptr;
        }
        return *this;
    }

    ~Book() {
        std::cout << "析构函数" << std::endl;
        delete[] data;
    }

private:
    int* data;
};

int main() 
{
    Book book1; // 调用默认构造函数
    Book book2 = book1; // 调用拷贝构造函数
    Book book3(std::move(book1)); // 调用移动构造函数
    book2 = book3; // 调用拷贝赋值运算符
    book3 = std::move(book2); // 调用移动赋值运算符

    return 0;
}

运行结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3. default关键字

当我们在C++中定义一个类的成员函数时,有时候我们希望使用默认的实现,而不需要自己去编写具体的实现。C++提供了一个default关键字,可以用来指示编译器生成默认的实现。

使用default关键字可以让编译器自动生成以下几种函数的默认实现:

  1. 默认构造函数:当我们没有定义任何构造函数时,编译器会自动生成一个默认构造函数。
  2. 拷贝构造函数:当我们没有定义拷贝构造函数时,编译器会自动生成一个默认的拷贝构造函数。
  3. 移动构造函数:当我们没有定义移动构造函数时,编译器会自动生成一个默认的移动构造函数。
  4. 拷贝赋值运算符:当我们没有定义拷贝赋值运算符时,编译器会自动生成一个默认的拷贝赋值运算符。
  5. 移动赋值运算符:当我们没有定义移动赋值运算符时,编译器会自动生成一个默认的移动赋值运算符。
  6. 析构函数:当我们没有定义析构函数时,编译器会自动生成一个默认的析构函数。

下面是一个示例代码,演示了如何使用default关键字来生成默认的构造函数和拷贝构造函数:

class MyClass 
{
public:
    MyClass() = default; // 默认构造函数
    MyClass(const MyClass& other) = default; // 拷贝构造函数

    void print() 
    {
        std::cout << "Hello, World!" << std::endl;
    }
};

int main() 
{
    MyClass obj1; // 调用默认构造函数
    MyClass obj2 = obj1; // 调用拷贝构造函数

    obj1.print(); // 输出: Hello, World!
    obj2.print(); // 输出: Hello, World!

    return 0;
}

运行结果:

img

在这个例子中,我们定义了一个名为MyClass的类。**我们使用default关键字来生成默认的构造函数和拷贝构造函数。**然后,我们创建了两个MyClass对象obj1和obj2,并调用了它们的print函数。

通过运行这段代码,我们可以看到两个对象的构造和拷贝构造函数被正确地调用,并且print函数输出了"Hello, World!"。

总之,default关键字可以让编译器自动生成默认的函数实现,避免我们手动编写这些函数的实现。这样可以简化代码,并且确保这些函数的行为符合预期。

4. delete关键字

在C++中,delete关键字用于删除某些特殊函数的默认实现。通过使用delete关键字,我们可以告诉编译器不要生成特定函数的默认实现。

使用delete关键字可以实现以下几种效果:

  1. 禁用默认构造函数:通过将构造函数声明为delete,我们可以禁用默认构造函数,使得对象不能被默认构造。
  2. 禁用拷贝构造函数和拷贝赋值运算符:通过将拷贝构造函数和拷贝赋值运算符声明为delete,我们可以禁用对象的拷贝操作。
  3. 禁用移动构造函数和移动赋值运算符:通过将移动构造函数和移动赋值运算符声明为delete,我们可以禁用对象的移动操作。

下面是一个示例代码,演示了如何使用delete关键字来禁用默认构造函数和拷贝构造函数:

class MyClass 
{
public:
    MyClass() = delete; // 禁用默认构造函数
    MyClass(const MyClass& other) = delete; // 禁用拷贝构造函数

    void print() 
	{
        std::cout << "Hello, World!" << std::endl;
    }
};

int main() 
{
    MyClass obj1; // 编译错误,禁用了默认构造函数
    MyClass obj2 = obj1; // 编译错误,禁用了拷贝构造函数

    return 0;
}

编译错误:

img

在这个例子中,我们定义了一个名为MyClass的类。我们使用delete关键字来禁用默认构造函数和拷贝构造函数。然后,我们尝试创建一个MyClass对象obj1和使用obj1初始化另一个对象obj2。

由于我们将默认构造函数和拷贝构造函数声明为delete,所以编译器会报错,提示我们禁用了这些函数,无法使用它们来创建对象。

总之,delete关键字可以用来禁用特定函数的默认实现,从而限制对象的某些操作。这样可以提高代码的安全性和可读性,确保对象的行为符合预期。

5. 可变参数模板

可变参数模板是C++11引入的一种模板特性,它允许我们定义一个可以接受任意数量和任意类型参数的模板函数或模板类。简单来说,它就像是一个可以接受不确定数量和类型的参数的函数。

举个例子来说明可变参数模板的使用。假设我们想要实现一个函数sum,可以计算任意数量的参数的和。我们可以使用可变参数模板来实现这个功能。

#include <iostream>

// 基本情况:没有参数时,返回0
int sum() {
    return 0;
}

// 递归情况:计算第一个参数和剩余参数的和
template<typename T, typename... Args>
T sum(T first, Args... rest) {
    return first + sum(rest...);
}

int main() {
    int result = sum(1, 2, 3, 4, 5);
    std::cout << "Sum: " << result << std::endl; // 输出: Sum: 15

    return 0;
}

在这个例子中,我们定义了一个可变参数模板函数sum。它有两个重载版本,一个是基本情况,当没有参数时返回0;另一个是递归情况,计算第一个参数和剩余参数的和。

在递归情况中,我们使用了模板参数包(template parameter pack)Args…来表示可变数量的参数。通过使用递归调用和展开参数包的方式,我们可以逐个计算参数的和。

在main函数中,我们调用sum函数,并传递了一系列整数作为参数。sum函数会计算这些整数的和,并将结果存储在result变量中,最后输出结果。

需要注意的是,可变参数模板还可以与其他模板特性(如模板参数推导、完美转发等)结合使用,以实现更加灵活和通用的功能。

**当没有第一个形参时,我们可以使用递归基准情况来处理。**这里举一个没有第一个形参的例子,假设我们想要实现一个函数print,可以打印出任意数量的参数。

#include <iostream>

// 递归基准情况:没有参数时,终止递归
void print() {
    std::cout << std::endl;
}

// 递归情况:打印第一个参数,然后递归打印剩余参数
template<typename T, typename... Args>
void print(T first, Args... rest) {
    std::cout << first << " ";
    print(rest...);
}

int main() {
    print(1, "hello", 3.14, 'a');
    // 输出: 1 hello 3.14 a

    return 0;
}

在这个例子中,我们定义了一个可变参数模板函数print。它有两个重载版本,一个是递归基准情况,当没有参数时终止递归;另一个是递归情况,它打印第一个参数,然后递归打印剩余参数。

在main函数中,我们调用print函数,并传递了一系列不同类型的参数。print函数会逐个打印这些参数,最后输出结果。

需要注意的是,递归基准情况在没有第一个形参时起到了终止递归的作用。这样,当参数传递完毕时,递归调用会停止,避免无限递归的问题。

这个例子展示了如何使用可变参数模板来实现一个可以打印任意数量参数的函数。通过递归调用和展开参数包的方式,我们可以逐个处理参数,实现更加灵活和通用的代码。

总之,可变参数模板是C++中的一种模板特性,允许我们定义可以接受任意数量和任意类型参数的模板函数或模板类。通过使用可变参数模板,我们可以实现更加灵活和通用的代码。

6. push_back和emplace_back的区别

当我们使用C++中的容器时,有两个常用的函数push_back和emplace_back,它们用于在容器的末尾插入元素。

**push_back函数接受一个参数,该参数是要插入的元素的副本。它将创建一个新的元素,并将其添加到容器的末尾。**例如,如果我们有一个vector容器,我们可以使用push_back函数将一个整数添加到容器的末尾。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec;

    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(3);

    for (int i : vec) {
        std::cout << i << " ";
    }
    // 输出: 1 2 3

    return 0;
}

**emplace_back函数与push_back函数类似,但它接受多个参数,并将这些参数传递给容器中的元素类型的构造函数。这意味着我们可以直接在容器中构造元素,而不需要创建副本。这在某些情况下可以提高性能。**例如,如果我们有一个vector容器,其中Person类有一个带有名称和年龄参数的构造函数,我们可以使用emplace_back函数直接在容器中构造一个新的Person对象。

#include <iostream>
#include <vector>
#include <string>

class Person {
public:
    Person(const std::string& name, int age) : name_(name), age_(age) {}

    void Print() {
        std::cout << "Name: " << name_ << ", Age: " << age_ << std::endl;
    }

private:
    std::string name_;
    int age_;
};

int main() {
    std::vector<Person> vec;

    vec.emplace_back("Alice", 25);
    vec.emplace_back("Bob", 30);
    vec.emplace_back("Charlie", 35);

    for (const Person& p : vec) {
        p.Print();
    }
    // 输出:
    // Name: Alice, Age: 25
    // Name: Bob, Age: 30
    // Name: Charlie, Age: 35

    return 0;
}

区别总结:

  • push_back函数接受一个参数,该参数是要插入的元素的副本。
  • emplace_back函数接受多个参数,并将这些参数传递给容器中的元素类型的构造函数,直接在容器中构造新的元素,而不需要创建副本。
  • 使用emplace_back函数可以避免创建副本,提高性能。

emplace_back并不总是比push_back更高效。它们的性能取决于具体的情况。

emplace_back的优势在于它可以直接在容器中构造对象,而不需要创建副本。这意味着它可以避免额外的对象复制或移动操作,从而提高性能。

**然而,emplace_back的效果取决于对象的构造函数。如果对象的构造函数非常复杂或开销很大,那么emplace_back可能会比push_back更慢。**因为emplace_back需要在容器中直接构造对象,而构造函数的复杂性可能会导致更多的计算和内存分配。

另外,emplace_back和push_back对于容器的内存分配也有不同的影响。emplace_back可以利用容器的内存,避免不必要的内存分配和拷贝,从而提高效率。而push_back则需要在容器中分配新的内存,并将对象复制或移动到新的内存中。

因此,要确定哪种方法更高效,需要根据具体的情况进行评估。在大多数情况下,emplace_back通常会更高效,特别是当对象的构造函数相对简单且开销较小时。但是在某些情况下,push_back可能会更适合,特别是当对象的构造函数比较复杂或开销较大时。

综上所述,emplace_back和push_back都有各自的优势和适用场景,具体使用哪种方法取决于具体的情况和需求。

6.1. emplace_back的优势

当我们在容器中使用emplace_back或push_back添加对象时,如果对象是通过移动构造函数构造的,将会更高效。移动构造函数可以避免不必要的数据复制,直接将资源从一个对象转移到另一个对象。

下面是一个示例,演示了如何在Person类中添加移动构造函数,并在构造函数中打印信息:

class Person {
public:
    Person(const std::string& name, int age)
        : name_(name), age_(age) {
        std::cout << "构造函数: " << name_ << ", " << age_ << std::endl;
    }

    Person(const Person& other)
        : name_(other.name_), age_(other.age_) {
        std::cout << "拷贝构造: " << name_ << ", " << age_ << std::endl;
    }

    Person(Person&& other)
        : name_(std::move(other.name_)), age_(other.age_) {
        std::cout << "移动构造: " << name_ << ", " << age_ << std::endl;
    }

    void Print() const {
        std::cout << "Name: " << name_ << ", Age: " << age_ << std::endl;
    }

private:
    std::string name_;
    int age_;
};

int main() {
    std::vector<Person> vec;

    vec.emplace_back("Alice", 25);
    vec.emplace_back("Bob", 30);
    vec.emplace_back("Charlie", 35);

    std::cout << std::endl;

    vec.push_back(Person("Dave", 40));
    vec.push_back(Person("Eve", 45));
    vec.push_back(Person("Frank", 50));

    for (const Person& p : vec) {
        p.Print();
    }

    return 0;
}

当运行上面的代码时,输出结果如下:

构造函数: Alice, 25
构造函数: Bob, 30
构造函数: Charlie, 35

移动构造: Dave, 40
构造函数: Dave, 40
移动构造: Eve, 45
构造函数: Eve, 45
移动构造: Frank, 50
构造函数: Frank, 50

Name: Alice, Age: 25
Name: Bob, Age: 30
Name: Charlie, Age: 35
Name: Dave, Age: 40
Name: Eve, Age: 45
Name: Frank, Age: 50

输出结果说明了对象的构造和移动过程:

  • 首先,通过emplace_back方法添加了三个对象,分别是Alice、Bob和Charlie。这些对象是通过构造函数直接在容器中构造的,没有进行拷贝或移动操作。
  • 然后,通过push_back方法添加了三个对象,分别是Dave、Eve和Frank。这些对象是通过移动构造函数从临时对象中移动构造而来的。
  • 最后,通过循环遍历容器中的对象,打印了每个对象的名称和年龄。

从输出结果可以看出,使用emplace_back方法构造对象时,并没有触发拷贝构造函数的调用,而使用push_back方法构造对象时,触发了移动构造函数的调用。

移动构造函数通过使用std::move来将资源从一个对象移动到另一个对象,避免了不必要的数据复制,提高了性能。

7. 包装器

在C++中,包装器是一种功能强大的工具,它可以将不同类型的可调用对象(如函数、函数指针、lambda表达式等)统一封装成一个对象,从而实现动态的函数调用。

想象一下你有三个不同的函数,分别是加法、减法和乘法。你希望能够在运行时动态地选择和调用这些函数。这时,你可以使用C++中的包装器来实现。

**首先,你需要使用std::function来声明一个变量,该变量可以存储任意具有相同参数和返回类型的可调用对象。**在我们的例子中,我们声明了一个std::function<int(int, int)>类型的变量operation,它可以存储两个整数参数并返回一个整数结果的可调用对象。

然后,你可以将不同的函数赋值给operation,以实现动态选择和调用不同的函数。在我们的例子中,我们分别将加法、减法和乘法函数赋值给operation。

最后,你可以使用operation来调用所选择的函数,并得到结果。在我们的例子中,我们分别使用加法、减法和乘法函数进行运算,并打印结果。

下面是相应的代码示例:

int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

int multiply(int a, int b) {
    return a * b;
}

int main() {
    // 使用add函数进行加法运算
    std::function<int(int, int)> operation = add;
    std::cout << "加法运算: " << operation(5, 3) << std::endl;

    // 使用subtract函数进行减法运算
    operation = subtract;
    std::cout << "减法运算: " << operation(5, 3) << std::endl;

    // 使用multiply函数进行乘法运算
    operation = multiply;
    std::cout << "乘法运算: " << operation(5, 3) << std::endl;

    return 0;
}

运行结果:

img

在这个例子中,我们定义了三个不同的函数add、subtract和multiply,它们都接受两个整数参数并返回一个整数结果。

然后,我们声明了一个std::function<int(int, int)>类型的变量operation,它可以存储任意具有相同参数和返回类型的可调用对象。

通过将不同的函数赋值给operation,我们可以在运行时动态选择和调用不同的函数。在主函数中,我们分别使用加法、减法和乘法函数进行运算,并打印结果。

通过使用包装器,我们可以在不改变代码结构的情况下,灵活地切换和组合不同的函数。这种灵活性对于实现策略模式、回调函数等场景非常有用。

7.1. 包装器的语法格式

当使用std::function包装器时,你需要指定可调用对象的签名(参数类型和返回类型),并将其作为模板参数传递给std::function类模板。语法格式如下:

std::function<返回类型(参数类型1, 参数类型2, ...)> 变量名;

其中,返回类型是可调用对象的返回类型,参数类型1, 参数类型2, …是可调用对象的参数类型,变量名是你给包装器变量取的名称。

下面是一些std::function包装器的常见用法:

  1. 将函数赋值给std::function变量:
std::function<int(int, int)> operation = add;

在这个例子中,我们将add函数赋值给了名为operation的std::function变量。这样,operation就可以被调用,并执行加法运算。

  1. 使用std::function调用可调用对象:
int result = operation(5, 3);

在这个例子中,我们使用operation调用了可调用对象(在这里是add函数),并将结果赋值给result变量。

  1. 重新赋值std::function变量:
operation = subtract;

在这个例子中,我们将subtract函数赋值给了之前的operation变量。这样,operation就可以被调用,并执行减法运算。

通过重新赋值std::function变量,我们可以在运行时动态地选择和调用不同的函数。

总而言之,std::function包装器提供了一种灵活的方式来存储和调用不同类型的可调用对象。通过使用std::function,我们可以实现动态选择和调用函数,从而提高代码的灵活性和可维护性。

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

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

相关文章

发表于《自然》杂志:语音转文本BCI的新突破实现62字/分钟的速度

语音脑机接口&#xff08;BCI&#xff09;是一项创新技术&#xff0c;通过用户的大脑信号在用户和某些设备之间建立通信通道&#xff0c;它们在恢复残疾患者的言语和通信能力方面具有巨大潜力。 早期的研究虽然很有希望&#xff0c;但尚未达到足够高的精度来解码大脑活动&…

深入MaxCompute -第十弹 -IF ELSE分支语句

简介&#xff1a; MaxCompute通过脚本模式支持IF ELSE分支语句&#xff0c;让程序根据条件自动选择执行逻辑&#xff0c;支持更好的处理因数据不同而需要采用不同策略的业务场景产生的复杂SQL&#xff0c;提高开发者编程的灵活性&#xff01; MaxCompute&#xff08;原ODPS&am…

正中优配:回购!回购!再回购!已成A股新常态?

上市公司回购潮还在继续&#xff01; 8月30日&#xff0c;海通证券、捷佳伟创等多家上市公司纷繁发布回购公告。自8月18日证监会提出“放宽相关回购条件&#xff0c;支撑上市公司展开股份回购”以来&#xff0c;A股上市公司掀起了一轮“回购潮”。Wind数据显现&#xff0c;8月…

Flutter小功能实现-咖啡店

1 导航栏实现 效果图&#xff1a; 1.Package google_nav_bar: ^5.0.6 使用文档&#xff1a; google_nav_bar | Flutter Package 2.Code //MyBottomNavBar class MyBottomNavBar extends StatelessWidget {void Function(int)? onTabChange;MyBottomNavBar({super.key, …

基于低代码/无代码工具构建 BI 应用程序

一、前言 随着数字化推进&#xff0c;越来越多的企业开始重视数据分析&#xff0c;希望通过BI&#xff08;商业智能&#xff09;技术提高业务决策的效率和准确性。 传统的BI解决方案往往需要大量的定制开发和数据准备&#xff0c;不仅周期长、成本高&#xff0c;还需要专业的数…

炒股技巧一文通!在深圳开股票账户交易佣金手续费一般是多少?

股票操作技巧可以说是股票投资成功的关键&#xff0c;下面是一些常用的技巧&#xff1a; 1.合理设置止盈止损&#xff1a;止盈可以帮助你在股票价格到达一定水平时获得利润&#xff0c;而止损可以保护你的本金&#xff0c;避免亏损过多。 2.选对个股&#xff1a;选对个股是根…

无涯教程-Android Mock Test函数

本节介绍了与 Android 相关的各种模拟测试。您可以在本地计算机上下载这些样本模拟测试,并在方便时离线解决。每个模拟测试均随附一个模拟测试键,可让您验证最终分数并为自己评分。 Mock Test I Mock Test II Mock Test III Mock Test IV Q 1 -什么是Android&#xff1f; A -A…

酒店的业务高度,关键还得看这个技术!

在酒店业务中&#xff0c;资产管理系统的重要性不容忽视。在这个竞争激烈且日益复杂的行业中&#xff0c;酒店经营者需要精确而高效地管理各种资源&#xff0c;包括客房、设备、财务以及人力。 资产管理系统为管理者提供了一个集中管理、优化和监控资产的强大工具。这不仅有助于…

产能紧张,联电、日月光急单要涨价 | 百能云芯

台积电在CoWoS先进封装领域的产能紧张&#xff0c;这导致英伟达在AI芯片方面的生产受到限制。有消息称&#xff0c;英伟达正考虑通过加价寻找除台积电以外的替代生产能力&#xff0c;以应对这一局面。这一消息引发了巨大的订单涌入效应。 联电公司作为提供CoWoS中间层材料的供应…

​放弃数据库,改用Kafka!

长期以来&#xff0c;数据库一直充当着记录系统&#xff0c;它们以可靠且持久的方式存储和管理关键数据&#xff0c;也赢得了大多数公司的信赖。 但时代在变。许多新兴趋势正在影响当今数据的存储和管理方式&#xff0c;不得不让一些技术决策者们重新考虑数据存储究竟还有哪些…

国标GB28181安防视频平台EasyGBS角色设备分配功能优化

视频流媒体安防监控国标GB28181平台EasyGBS视频能力丰富&#xff0c;部署灵活&#xff0c;既能作为业务平台使用&#xff0c;也能作为安防监控视频能力层被业务管理平台调用。国标GB28181视频监控EasyGBS平台可提供流媒体接入、处理、转发等服务&#xff0c;支持内网、公网的安…

2023年全方位SaaS平台测评!SaaS平台应该怎么选择?

什么是SaaS平台&#xff1f;SaaS平台的优势在哪&#xff1f;怎么样选择SaaS平台&#xff1f;作为两个在SaaS平台领域做的非常优秀的资深平台&#xff0c;简道云和salesforce究竟能更胜一筹&#xff1f;本篇&#xff0c;我将带领大家深入测评这两款SaaS平台&#xff0c;话不多说…

Jmter生成MD5 jmter使用md5 jmter使用自定义参数 jmter生成自定义参数 jmter编写java代码

Jmter生成MD5 jmter使用md5 jmter使用自定义参数 jmter生成自定义参数 jmter编写java代码 1、创建一个线程组2、创建线程组 http请求3、在 http请求添加前置处理器(BeanShell)4、请求测试 是否生效4.1 GET请求4.2 POST请求 1、创建一个线程组 2、创建线程组 http请求 在线程组…

linux云服务器安装宝塔

首先远程连接服务器 yum update -y 拉取宝塔面板镜像并安装 #CentOS7以上: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh #CentOS7以下: #yum install -y wget && wget -O install.sh…

一体化数据安全平台 uDSP 获“金鼎奖”优秀金融科技解决方案奖

近日&#xff0c;2023 年中国国际金融展“金鼎奖”评选结果揭晓&#xff0c;原点安全打造的“一体化数据安全平台 uDSP”产品获评“金鼎奖”优秀金融科技解决方案奖。该产品目前已广泛应用于银行业、保险企业、证券、医疗、互联网、政务、在线教育等诸多领域。此次获奖再次印证…

关闭浏览器的跨域校验

首发博客地址 问题描述 当你访问资源失败&#xff0c;并遇到以下类似提示时&#xff1a; Access to script at 资源路径 from origin null has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrom…

Vue2-replace属性、编程式路由导航、缓存路由组件、两个新的生命周期钩子、路由守卫、路由器工作模式

&#x1f954;&#xff1a;如果事与愿违&#xff0c;那一定是上天另有安排 更多Vue知识请点击——Vue.js VUE2-Day13 router-link的replace属性编程式路由导航1、什么是编程式路由导航2、如何编码3、使用案例示例说明 缓存路由组件两个新的生命周期钩子路由守卫1、路由元信息2、…

带纽扣电池产品出口澳洲安全标准,纽扣电池IEC 60086认证

澳大利亚政府公布了《消费品&#xff08;纽扣/硬币电池&#xff09;安全标准》和《消费品&#xff08;纽扣/硬币电池&#xff09;信息标准》。届时出口纽扣/硬币电池以及含有纽扣/硬币电池产品到澳大利亚的供应商&#xff0c;必须遵守这些标准中的要求。 一、 安全标准及信息标…

实力征服每寸版图|昂首资本Anzo Capital狂揽“年度最佳经纪商“和“最佳青年导师”双料大奖

棉兰老岛交易者博览会在Tibungco 的 RTC-KorPhil 职业培训中心多功能厅举办。在本次博览会上&#xff0c;Anzo Capital 昂首资本脱颖而出斩获“2023年度最佳经纪商”&#xff0c;Anzo Capital 官方金融教育专家Joseph Lejarde斩获“最佳青年导师”双料大奖。 棉兰老岛交易者…

解决Apache Tomcat “Request header is too large“ 异常 ‍

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…