C++11学习笔记(3)——通用工具(上)(包含重要特性智能指针Smart pointer)

1.Pair

在C++11中,std::pair是一个模板类,用于将两个值组合成一个单元。它可以将两个不同的类型的值配对在一起,并且提供了对这对值的访问和操作。

std::pair的定义

template<class T1, class T2>
struct pair{
	T1 first;
	T2 second;
};

一些用法

创建和初始化:

可以使用构造函数或花括号初始化列表来创建和初始化std::pair对象。例如:

std::pair<int, std::string> myPair(42, "Hello");
std::pair<double, bool> anotherPair = {3.14, true};

访问成员

std::pair对象的成员可以通过.first和.second进行访问。例如:

std::pair<int, std::string> myPair(42, "Hello");
int x = myPair.first;
std::string str = myPair.second;

比较和排序

std::pair可以进行比较操作,根据.first和.second的值进行比较。std::pair对象可以在容器中进行排序。例如:

std::pair<int, std::string> pair1(42, "Hello");
std::pair<int, std::string> pair2(10, "World");

bool result = (pair1 < pair2);  // 比较操作
std::vector<std::pair<int, std::string>> myVector = {pair1, pair2};
std::sort(myVector.begin(), myVector.end());  // 容器排序

使用范例:

std::pair常常用于返回多个值的函数,以及在需要将两个值作为单个单元传递的情况下。例如:

std::pair<int, std::string> getPerson() {
    int age = 25;
    std::string name = "John";
    return std::make_pair(age, name);
}

std::pair<int, int> divideAndRemainder(int dividend, int divisor) {
    int quotient = dividend / divisor;
    int remainder = dividend % divisor;
    return {quotient, remainder};
}

make_pair()

无需写出类型就能生成一个pair对象,例如:

std::pair<int, char> myPair(42, "Hello");
std::make_pair(42, "Hello");

操作函数

在这里插入图片描述

std::pair提供了一种便捷的方式来组合两个值,并且可以在多种场景下使用。它是C++中常用的工具之一,用于简化代码和提高代码的可读性。

2.Tuple

Tuple扩展了pair的概念,拥有任意数量的元素,其中每个类型都可以被指定。

Tuple的定义

template<typename... Types>
class tuple;

Tuple示例

#include <iostream>
#include <tuple>

int main() {
    // 创建一个包含整数、字符串和浮点数的元组
    std::tuple<int, std::string, double> myTuple(42, "Hello", 3.14);

    // 访问元组中的元素
    int intValue = std::get<0>(myTuple);
    std::string stringValue = std::get<1>(myTuple);
    double doubleValue = std::get<2>(myTuple);

    // 修改元组中的元素
    std::get<0>(myTuple) = 100;

    // 使用tie函数将元组的元素解包到变量中
    
    int a;
    std::string b;
    double c;
    std::tie(a, b, c) = myTuple;
		//std::tie(a, std::ignore, c) = myTuple;忽略某些元素
    // 打印元组的元素
    std::cout << "Tuple elements: " << a << ", " << b << ", " << c << std::endl;
		//c++11也可直接输出myTuple
    return 0;
}

操作函数

在这里插入图片描述

std::tuple_size

用于获取std::tuple的大小。

#include <iostream>
#include <tuple>

int main() {
    std::tuple<int, std::string, double> myTuple;

    std::cout << "Tuple size: " << std::tuple_size<decltype(myTuple)>::value << std::endl;

    return 0;
}

输出结果为:Tuple size: 3,表示std::tuple中有三个元素。

std::tuple_element

用于获取std::tuple中指定位置的元素类型。


#include <iostream>
#include <tuple>

int main() {
    using MyTuple = std::tuple<int, std::string, double>;

    std::tuple_element<1, MyTuple>::type myElement;

    std::cout << "Element type: " << typeid(myElement).name() << std::endl;

    return 0;
}

输出结果为:Element type: NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE,表示std::tuple的第二个元素类型为std::string。

std::tuple_cat

用于将多个std::tuple合并成一个大的std::tuple。


#include <iostream>
#include <tuple>

int main() {
    std::tuple<int, std::string> tuple1(42, "Hello");
    std::tuple<double> tuple2(3.14);

    auto combinedTuple = std::tuple_cat(tuple1, tuple2);

    std::cout << "Combined tuple size: " << std::tuple_size<decltype(combinedTuple)>::value << std::endl;

    return 0;
}

输出结果为:Combined tuple size: 3,表示将tuple1和tuple2合并后,得到了一个包含三个元素的std::tuple。

pair与tuple

从std::pair到std::tuple的转换

可以使用std::make_tuple函数将std::pair转换为std::tuple。

#include <iostream>
#include <tuple>

int main() {
    std::pair<int, double> myPair(42, 3.14);

    std::tuple<int, double> myTuple = std::make_tuple(myPair.first, myPair.second);

    std::cout << "Tuple elements: " << std::get<0>(myTuple) << ", " << std::get<1>(myTuple) << std::endl;

    return 0;
}

在上述示例中,我们有一个std::pair<int, double>类型的对象myPair,我们可以使用std::make_tuple将其转换为std::tuple<int, double>类型的对象myTuple。

从std::tuple到std::pair的转换

可以使用std::get函数将std::tuple的元素提取出来,并使用这些元素创建一个std::pair。


#include <iostream>
#include <tuple>

int main() {
    std::tuple<int, double> myTuple(42, 3.14);

    std::pair<int, double> myPair = std::make_pair(std::get<0>(myTuple), std::get<1>(myTuple));

    std::cout << "Pair elements: " << myPair.first << ", " << myPair.second << std::endl;

    return 0;
}

在上述示例中,我们有一个std::tuple<int, double>类型的对象myTuple,我们可以使用std::get函数将其元素提取出来,并使用这些元素创建一个std::pair<int, double>类型的对象myPair。

3.Smart pointer智能指针***

指针是c/c++的重要特性,但使用中常常会出现空悬,多次删除,资源泄露等问题,避免这些问题的一个通常做法是使用智能指针。
自C++11起,标准库提供两大类智能指针:shared_ptr和unique_ptr,而auto_ptr被弃用,它们定义在< memory >内

shared_ptr

std::shared_ptr作为智能指针的一种类型,用于更安全和方便地管理动态分配的内存。std::shared_ptr使用引用计数的方式来跟踪资源的所有者,并在不再需要时自动释放资源。

以下是std::shared_ptr的基本用法和示例:

创建std::shared_ptr对象:

可以使用std::make_shared函数或直接使用std::shared_ptr的构造函数来创建std::shared_ptr对象。

#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> ptr1 = std::make_shared<int>(42);
    std::shared_ptr<int> ptr2(new int(100));

    std::cout << *ptr1 << std::endl; // 输出:42
    std::cout << *ptr2 << std::endl; // 输出:100

    return 0;
}

在上述示例中,我们使用std::make_shared创建了一个包含整数值的std::shared_ptr对象ptr1,以及使用std::shared_ptr的构造函数创建了另一个std::shared_ptr对象ptr2。

共享拥有资源:

可以将一个std::shared_ptr赋值给另一个std::shared_ptr,这样它们会共享对同一资源的拥有权。

#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> ptr1 = std::make_shared<int>(42);
    std::shared_ptr<int> ptr2 = ptr1;

    std::cout << *ptr1 << std::endl; // 输出:42
    std::cout << *ptr2 << std::endl; // 输出:42

    return 0;
}

在上述示例中,我们将ptr1赋值给ptr2,它们现在都指向同一个整数资源,并且共享对该资源的拥有权。

引用计数和资源释放:

std::shared_ptr使用引用计数来跟踪资源的所有者数量。当最后一个std::shared_ptr析构或被赋予新的值时,引用计数会减少并检查是否需要释放资源。

#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> ptr1 = std::make_shared<int>(42);
    std::shared_ptr<int> ptr2 = ptr1;
    std::shared_ptr<int> ptr3 = ptr1;

    std::cout << *ptr1 << std::endl; // 输出:42
    std::cout << *ptr2 << std::endl; // 输出:42
    std::cout << *ptr3 << std::endl; // 输出:42

    ptr1.reset();

    std::cout << std::boolalpha;
    std::cout << "ptr1 is nullptr: " << (ptr1 == nullptr) << std::endl; // 输出:true
    std::cout << "ptr2 is nullptr: " << (ptr2 == nullptr) << std::endl; // 输出:false
    std::cout << "ptr3 is nullptr: " << (ptr3 == nullptr) << std::endl; // 输出:false

    return 0;
}

在上述示例中,我们创建了三个std::shared_ptr对象ptr1、ptr2和ptr3,它们都指向同一个整数资源。当ptr1调用reset函数后,它不再拥有资源,引用计数减少并且资源被释放,但ptr2和ptr3仍然有效并拥有资源。

std::shared_ptr还提供了其他有用的成员函数,如get(返回底层指针)、use_count(返回引用计数)、unique(检查是否是唯一的拥有者)等。

使用std::shared_ptr可以有效避免内存泄漏和悬空指针等问题,并提供方便的资源管理机制。然而,要注意避免循环引用问题,因为它可能导致资源无法释放。

析构函数

std::shared_ptr的析构函数是由其模板参数指定的删除器(deleter)来执行的。可以通过提供自定义的删除器来自定义析构行为。

删除器是一个可调用对象,用于在std::shared_ptr的引用计数归零时执行资源的释放。删除器可以是函数指针、函数对象、Lambda表达式或自定义类型的对象。

以下是使用自定义删除器的示例:

#include <iostream>
#include <memory>

// 自定义删除器
struct CustomDeleter {
    void operator()(int* ptr) {
        std::cout << "Custom deleter is called" << std::endl;
        delete ptr;
    }
};

int main() {
    std::shared_ptr<int> ptr(new int(42), CustomDeleter());

    return 0;
}

在上述示例中,我们定义了一个名为CustomDeleter的结构体,其中重载了圆括号运算符,以实现自定义的删除行为。在main函数中,我们使用std::shared_ptr的构造函数来创建一个std::shared_ptr对象ptr,并将自定义删除器作为参数传递。

当std::shared_ptr的引用计数归零时,会调用自定义删除器的圆括号运算符来释放资源,并执行我们定义的自定义删除行为。在这个示例中,自定义删除器会输出一条消息,并删除指向整数的指针。

注意,当提供自定义删除器时,需要确保删除器与指针类型兼容,并遵循适当的资源释放规则。通过自定义删除器,可以实现更灵活的资源管理和析构行为,以满足特定的需求。

其他操作

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

weak_ptr

C++11引入了std::weak_ptr作为一种智能指针类型,用于解决std::shared_ptr可能导致的循环引用问题。std::weak_ptr允许对由std::shared_ptr管理的对象进行弱引用,而不会增加引用计数,也不会阻止对象的销毁。

以下是std::weak_ptr的基本用法和示例:

创建std::weak_ptr对象

可以通过将std::shared_ptr对象转换为std::weak_ptr来创建std::weak_ptr对象。

#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);
    std::weak_ptr<int> weakPtr = sharedPtr;

    // 输出:42
    if (auto lockedPtr = weakPtr.lock()) {
        std::cout << *lockedPtr << std::endl;
    } else {
        std::cout << "Resource has been released" << std::endl;
    }

    return 0;
}

在上述示例中,我们创建了一个std::shared_ptr对象sharedPtr来管理一个整数资源,并通过将其转换为std::weak_ptr创建了一个std::weak_ptr对象weakPtr。注意,转换为std::weak_ptr不会增加资源的引用计数。

检查std::weak_ptr是否有效并访问资源

可以使用lock()函数来检查std::weak_ptr是否有效,并获取对资源的共享访问。

#include <iostream>
#include <memory>

int main() {
    std::weak_ptr<int> weakPtr;

    {
        std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);
        weakPtr = sharedPtr;

        // 输出:42
        if (auto lockedPtr = weakPtr.lock()) {
            std::cout << *lockedPtr << std::endl;
        } else {
            std::cout << "Resource has been released" << std::endl;
        }
    }

    // 输出:"Resource has been released"
    if (auto lockedPtr = weakPtr.lock()) {
        std::cout << *lockedPtr << std::endl;
    } else {
        std::cout << "Resource has been released" << std::endl;
    }

    return 0;
}

在上述示例中,我们在作用域中创建了一个std::shared_ptr对象sharedPtr,并将其转换为std::weak_ptr对象weakPtr。在作用域结束后,sharedPtr被销毁,资源被释放。在后续的代码中,我们通过调用lock()函数检查weakPtr是否有效,并访问资源。如果weakPtr有效,lock()函数将返回一个有效的std::shared_ptr对象,否则返回一个空指针。

查看资源

expired()

用于检查std::weak_ptr是否过期(即指向的资源是否已经被释放)。如果资源已经被释放,则返回true;否则返回false。

use_count()

用于获取与std::weak_ptr共享相同资源的有效std::shared_ptr对象的数量。

#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);
    std::weak_ptr<int> weakPtr = sharedPtr;

    // 输出:1
    std::cout << "use_count: " << sharedPtr.use_count() << std::endl;

    if (weakPtr.expired()) {
        std::cout << "Resource has been released" << std::endl;
    } else {
        // 输出:42
        std::cout << "Value: " << *weakPtr.lock() << std::endl;
    }

    sharedPtr.reset();

    // 输出:0
    std::cout << "use_count: " << weakPtr.use_count() << std::endl;

    if (weakPtr.expired()) {
        std::cout << "Resource has been released" << std::endl;
    } else {
        std::cout << "Value: " << *weakPtr.lock() << std::endl;
    }

    return 0;
}

在上述示例中,我们创建了一个std::shared_ptr对象sharedPtr来管理一个整数资源,并通过将其转换为std::weak_ptr对象weakPtr来创建一个弱引用。我们使用use_count()函数来获取与sharedPtr共享相同资源的有效std::shared_ptr对象的数量。在if语句中,我们使用expired()函数检查weakPtr是否过期,然后使用lock()函数获取有效的std::shared_ptr对象并输出其值。在后续的代码中,我们通过调用reset()函数将sharedPtr置空,释放资源,并检查weakPtr是否过期。

需要注意的是,由于std::weak_ptr不增加引用计数,所以调用use_count()函数返回的是与其共享资源的有效std::shared_ptr对象的数量。

其他操作

在这里插入图片描述

通过使用std::weak_ptr,我们可以避免std::shared_ptr可能导致的循环引用问题,并更灵活地管理资源的生命周期。

unique_ptr

std::unique_ptr是独占所有权的智能指针,意味着同一时间只能有一个std::unique_ptr拥有指针所指向的对象。当unique_ptr被销毁,其所指向的对象也会自动被销毁。

以下是std::unique_ptr的基本用法和示例:

创建std::unique_ptr对象

可以使用std::make_unique函数或直接使用std::unique_ptr的构造函数来创建std::unique_ptr对象。

#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int> uniquePtr1 = std::make_unique<int>(42);
    std::unique_ptr<int> uniquePtr2(new int(100));

    // 输出:42
    std::cout << *uniquePtr1 << std::endl;

    // 输出:100
    std::cout << *uniquePtr2 << std::endl;

    return 0;
}

在上述示例中,我们使用std::make_unique函数和构造函数分别创建了两个std::unique_ptr对象uniquePtr1和uniquePtr2来管理两个整数资源。注意,std::make_unique是C++14引入的函数,如果你使用的是C++11,可以直接使用std::unique_ptr的构造函数。

执行所有权的转移

std::unique_ptr具有独占所有权的特性,可以通过移动语义将所有权从一个std::unique_ptr转移到另一个std::unique_ptr。

#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int> uniquePtr1 = std::make_unique<int>(42);
    std::unique_ptr<int> uniquePtr2;

    uniquePtr2 = std::move(uniquePtr1);

    // 输出:42
    std::cout << *uniquePtr2 << std::endl;

    return 0;
}

在上述示例中,我们通过使用std::move将uniquePtr1的所有权转移到uniquePtr2。这样,uniquePtr2现在拥有原始资源,并且uniquePtr1不再拥有资源。

使用自定义删除器

可以通过提供自定义删除器来指定std::unique_ptr在释放资源时的行为。删除器是一个函数对象或函数指针,用于定义资源释放的方式。
#include <iostream>
#include <memory>

struct CustomDeleter {
    void operator()(int* ptr) {
        std::cout << "Deleting resource: " << *ptr << std::endl;
        delete ptr;
    }
};

int main() {
    std::unique_ptr<int, CustomDeleter> uniquePtr(new int(42));

    // 输出:42
    std::cout << *uniquePtr << std::endl;

    return 0;
}

在上述示例中,我们创建了一个带有自定义删除器CustomDeleter的std::unique_ptr对象uniquePtr。当uniquePtr被销毁时,自定义删除器将被调用,并负责释放资源。

std::unique_ptr提供了一种轻量级的智能指针,适用于管理单个所有权的对象,提供了高效的内存管理和安全的资源释放。

关于array

C++删除array需要使用delete[],由于C++无法区分pointer指向的是单个对象还是array,因此指针自动删除会出错,标准库对此提供了特殊版本。
在C++11中,引入了std::unique_ptr的数组版本,即std::unique_ptr<T[]>,用于管理动态分配的数组对象。

下面是std::unique_ptr管理动态数组的示例:

#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int[]> uniquePtr(new int[5]);

    for (int i = 0; i < 5; i++) {
        uniquePtr[i] = i + 1;
    }

    for (int i = 0; i < 5; i++) {
        std::cout << uniquePtr[i] << " ";
    }

    // 输出:1 2 3 4 5
    std::cout << std::endl;

    return 0;
}

在上述示例中,我们使用std::unique_ptr<int[]>创建了一个std::unique_ptr对象uniquePtr来管理一个包含5个整数的动态数组。通过使用数组下标运算符[],我们可以访问和修改数组中的元素。当uniquePtr被销毁时,它会自动释放动态数组所占用的内存。

需要注意的是,std::unique_ptr<T[]>只适用于管理通过new T[]动态分配的数组,而不是指向已存在的数组,这个版本不提供操作符’*‘和’->'。在使用std::unique_ptr<T[]>时,不需要手动调用delete[]释放内存,std::unique_ptr会自动处理内存的释放。另外,这一版本不支持不同类型直接的转换,不允许指向派生元素类型。

其他操作

在这里插入图片描述

auto_ptr

std::auto_ptr是C++98标准引入的智能指针,用于管理动态分配的对象。然而,自从C++11起,std::auto_ptr已经被废弃,不推荐在新代码中使用,因为它存在一些缺陷和不安全的行为。
以下是一些关于std::auto_ptr的重要注意事项:

所有权的转移

与std::unique_ptr不同,std::auto_ptr支持所有权的转移,即可以将资源的所有权从一个std::auto_ptr转移到另一个std::auto_ptr。这意味着在转移所有权后,原始的std::auto_ptr将不再拥有资源。

不支持数组

std::auto_ptr只适用于管理单个对象,而不支持管理动态分配的数组。

删除器的限制

std::auto_ptr只支持使用默认的删除器,无法自定义删除器。这意味着在销毁std::auto_ptr时,只会调用delete来释放资源。

不安全的拷贝语义

std::auto_ptr的拷贝语义存在问题,它使用的是移动语义而不是传统的拷贝语义。这导致在拷贝后,原始的std::auto_ptr会失去对资源的所有权,可能导致资源的重复释放。

因此,建议使用C++11引入的更安全和更强大的智能指针类型,如std::unique_ptr用于独占所有权的情况,std::shared_ptr用于共享所有权的情况,以及std::weak_ptr用于解决循环引用问题。这些智能指针类型提供了更好的语义和更强的类型检查,能够更好地管理资源并提供更好的内存安全性。

补充

内存开销

相比于裸指针,智能指针通常会引入一定的内存开销。智能指针对象通常包含引用计数等额外的数据成员,这可能会占用更多的内存。虽然这个开销在大多数情况下可以忽略不计,但对于资源非常有限的嵌入式系统等特殊环境下,需要仔细考虑智能指针的使用。

不适合某些情况

智能指针并不是适用于所有情况的通用解决方案。在某些特定的应用场景下,例如与C接口进行交互、处理外部资源等,可能需要手动管理资源,而不适合使用智能指针。

语义差异和注意事项

不同类型的智能指针有不同的语义和行为,需要理解和注意其使用方式。例如,std::shared_ptr的共享所有权可能带来额外的开销和线程安全的问题,而std::unique_ptr则适用于独占所有权的场景。此外,智能指针的使用还需要注意循环引用、空指针检查、潜在的性能影响等问题。

虽然智能指针有其缺陷和注意事项,但它们在大多数情况下提供了方便、安全和可靠的资源管理方式。使用智能指针时,需要理解其语义和行为,并结合具体的应用场景进行选择和使用。

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

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

相关文章

【JAVA开发环境配置】 卸载JDK很简单, 一分钟帮你搞定!

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&#x1…

Context Prior for Scene Segmentation--CVPR, 2020

Context Prior for Scene Segmentation–CVPR, 2020 文章目录 Context Prior for Scene Segmentation--CVPR, 2020一、背景介绍二、方法介绍1.A的生成2.Affinity Loss3.如何从 X X X获取P4.Y操作 一、背景介绍 问题&#xff1a;现阶段&#xff0c;不少语义分割方法所限于卷积结…

Android12之如何查看hidl服务(一百五十五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

软件项目管理需要具备哪些能力?

作为一名软件项目管理者&#xff0c;在处理许多事情时需要不断提高个人在数据分析处理、项目业务流程管理等各个领域的能力。当然作为过来人&#xff0c;我也很清楚很多软件项目管理新人也较为疑惑如何提高自己的能力和专业水平&#xff0c;以便提高工作效率。那我也想与大家唠…

开源反分裂与数字大同世界

这是一篇报告的读后感&#xff0c;这篇报告是由Linux基金会研究部门发布的&#xff0c;名为《助力全球协作——开源代码的领导者如何面对分裂的挑战》。 这份报告的主要内容包括&#xff1a; - 开源代码开发中的分裂及其利弊- 开源的国际化&#xff0c;以及项目领导者如何克服参…

Python3数据分析与挖掘建模(16)特征降维与特征衍生

1. 特征降维&#xff08;PCA&#xff09; 回顾知识点&#xff1a; 特征降维是指将高维特征空间的数据映射到低维空间的过程&#xff0c;以减少特征的数量并保留数据的主要信息。下面是特征降维的一般步骤&#xff1a; &#xff08;1&#xff09;求特征协方差矩阵&#xff1a…

可调电源LM317 的内部原理 - 特殊的电压跟随器

之前一直没想过这类LDO 内部是怎么整的&#xff0c;它似乎是用一个分压电路采集它输出的电压作为参考&#xff0c;然后却能把输出电压稳定下来&#xff0c;颇有种左脚踩右脚上天的意思。典型的LM317 电路如下&#xff1a; 如果是个普通的电压跟随器&#xff0c;无论是基于三极管…

牛客小白月赛56

今天无聊vp了一下 A.省略 B.最优肯定是全部都是1 C.直接统计每个余数下可以填多少个数&#xff0c;然后排序从小到大的排序输出即可 #include <iostream> #include <cstring> #include <algorithm> #include <vector> #include <queue> #inc…

【八大排序(六)】快排终极篇-快速排序非递归版

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:八大排序专栏⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习排序知识   &#x1f51d;&#x1f51d; 快排非递归版 1. 前情回顾2. 快排非递归基…

windows10家庭版禁用Device/Credential Guard解决方案

文章目录 背景&#xff08;禁用的原因&#xff09;解决的方式方式一&#xff1a;通过Windows本身的功能设置禁用 ( 非家庭版的使用)1. 禁用Device Guard或Credential Guard&#xff1a;2. 关闭Hyper-V选项3. 重启电脑 方式二&#xff1a;通过命令关闭Hyper-V ( Windows 10家庭版…

java三大特性之【多态】

多态 1.1 概念1.2 实现条件1.3 方法重写&#xff08;override&#xff09;与方法重载&#xff08;overload&#xff09;1.4 向上转型1.5 向下转型 1.1 概念 同样的一个方法/行为&#xff0c;经过不同的对象&#xff0c;表现出不同的行为&#xff0c;这样的现象就称为多态。 举…

二叉搜索树之AVL树

目录 1.概念 2.定义 3.插入 4.旋转 1. 新节点插入较高左子树的左侧---右单旋 2. 新节点插入较高右子树的右侧---左单旋 3. 新节点插入较高左子树的右侧&#xff1a;先左单旋再右单旋【左右双旋】 4. 新节点插入较高右子树的左侧---右左&#xff1a;先右单旋再左单旋【右…

I.MX6ULL_Linux_驱动篇(37) linux系统定时器

定时器是我们最常用到的功能&#xff0c;一般用来完成定时功能&#xff0c;本章我们就来学习一下 Linux 内核提供的定时器 API 函数&#xff0c;通过这些定时器 API 函数我们可以完成很多要求定时的应用。 Linux内核也提供了短延时函数&#xff0c;比如微秒、纳秒、毫秒延时函数…

Car Guide

文章目录 科目一第一章 机动车驾驶证申领和使用规定第一节 驾驶证的许可&#xff1f;种类和有效期第二节 驾驶证的申领第三节 驾驶证的使用第四节 驾驶考试第五节 违法记分制度 第二章 交通信号第一节 交通信号灯第二节 交通标志第三节 交通标线第四节 交警手势 第三章 道路交通…

【编程语言 · C语言 · 递归函数】

递归函数 C 语言的函数都支持递归, 也就是说&#xff0c;每个函数都可以直接或者间接第调用自己。所谓的间接调用&#xff0c;是指在递归函数调用的下层函数中再调用自己。 递归关系图如下&#xff1a; 递归之所以能实现&#xff0c;是因为函数的每个执行过程在栈中都有自己的…

Redis从入门到精通之底层数据结构快表QuickList详解

文章目录 0.前言1. 快表的结构2. Redis 6.0 快表quicklist 基本结构2.1 成员变量2.1 主要操作2.1 推导结果 3. 快表的操作 3. 快表的优缺点3.1 优点&#xff1a;3.2 缺点&#xff1a; 5. Redis从入门到精通系列文章 0.前言 上个篇章回顾&#xff0c;我们上个章节&#xff0c;讲…

Win10 系统专业版远程桌面如何才能多用户同时登录使用?

环境&#xff1a; Win10专业版19041 RDPWrap-v1.6.2 dell5493笔记本 问题描述&#xff1a; Win10 系统专业版远程桌面如何才能多用户同时登录使用&#xff1f; 解决方案&#xff1a; 安装RDPWrap 1.关闭remote desktop services服务 安装RDP之前&#xff0c;要先关闭re…

Kuberentes,k8s诞生简介

一、前言 什么是k8s&#xff1f; Kuberentes 是基于容器的集群管理平台&#xff0c;它的简称&#xff0c;是K8S。有人说之所以叫k8s&#xff0c;是因为k到s中间有8个字母&#xff0c;因此叫k8s&#xff0c;也有人说&#xff0c;在使用k8s的安装配置流程中&#xff0c;共分为8…

验证attention是否在图像分类问题上起决定性作用

来源&#xff1a;投稿 作者&#xff1a;摩卡 编辑&#xff1a;学姐 Motivation 现阶段出现了大量的Transformer-style图像分类模型&#xff0c;并且这些模型在ImageNet上取得了不俗的成绩&#xff0c;这些Transformer-style模型将取得高性能的功劳归功于Multi-head attention注…

12.异常检测

12.1 异常检测的应用 异常检测最常见的应用是欺诈检测&#xff1b; 如果你有很多用户&#xff0c;每个用户都在从事不同的的活动&#xff0c;你可以对不同的用户活动计算特征变量&#xff0c;然后可以建立一个模型来表示用户表现出各种行为的可能性&#xff0c;用来表示用户行…