【C++11】C++11类与模板语法的完善

news2024/12/25 15:15:18

目录

一,新的类功能

1-1,默认成员函数

1-2,强制生成关键字

二,可变参数模板

2-1,模板参数包

2-2,STL容器empalce的相关接口


一,新的类功能

1-1,默认成员函数

        C++11之前的类中有6个默认成员函数(默认成员函数就是我们不写编译器会生成一个默认的):1. 构造函数。2. 析构函数。3. 拷贝构造函数。4. 拷贝赋值重载。5. 取地址重载。6. const 取地址重载。这六个之中重要的是前4个,后两个取地址的用处不大。

        C++11新增了两个:移动构造函数移动赋值运算符重载。此两种默认成员函数与其它的默认成员函数有所不同,需说明以下三点。

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

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

        3,如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值,这时需自己实现拷贝构造和拷贝赋值。因为当你自己编写移动构造或移动赋值后,说明你可能想要完全控制对象的拷贝行为,且有了移动语义通常也需要深拷贝,而默认的拷贝操作都是浅拷贝,所以为了确保对象的拷贝行为符合预期,有了移动语义后编译器不会默认生成拷贝构造和拷贝赋值。

        这里的代码演示需借助类结构演示,我们先简单设计String类,如下:

#include <cstring>
#include <iostream>
using namespace std;
namespace bit
{
    class string
    {
    public:
        //构造函数
        string(const char* str = "")
            :_size(strlen(str))
            , _capacity(_size)
        {
            cout << "string(char* str)" << endl;
            _str = new char[_capacity + 1];
            strcpy(_str, str);
        }
        //拷贝构造
        string(const string& s)
            :_str(nullptr)
        {
            cout << "string(const string& s) -- 深拷贝" << endl;
            string tmp(s._str);
            swap(tmp);
        }
        //赋值重载
        string& operator=(const string& s)
        {
            cout << "string& operator=(string s) -- 深拷贝" << endl;
            string tmp(s);
            swap(tmp);
            return *this;
        }
        //移动构造
        string(string&& s)
            :_str(nullptr)
            , _size(0)
            , _capacity(0)
        {
            cout << "string(string&& s) -- 移动语义" << endl;
            swap(s);
        }
        //移动赋值
        string& operator=(string&& s)
        {
            cout << "string& operator=(string&& s) -- 移动语义" << endl;
            swap(s);
            return *this;
        }
        //析构函数
        ~string()
        {
            delete[] _str;
            _str = nullptr;
        }
    private:
        void swap(string& s)
        {
            std::swap(_str, s._str);
            std::swap(_size, s._size);
            std::swap(_capacity, s._capacity);
        }
        char* _str;
        size_t _size;
        size_t _capacity; 
    };
}

        上面string类中自己实现了移动构造和移动赋值,编译器不会默认生成拷贝构造和拷贝赋值,若这里不自己实现拷贝构造和拷贝赋值,将不能实现有关拷贝构造和拷贝赋值的语句。下面来用以上类观察默认移动构造和默认移动赋值。

#include "String.h" //设计string的头文件
class Person
{
public:
    Person(const char* name = "", int age = 0)
        :_name(name) , _age(age)
    {    }
private:
    bit::string _name; 
    int _age;
};

/*自定义string类中实现了移动语义,不会生成默认拷贝构造和默认拷贝赋值,若自己不实现的话这里将会出错*/
int main()
{
    Person s1;
    cout << endl;
    //拷贝构造
    Person s2 = s1;
    cout << endl;
    
    //移动构造

    /*Preson满足默认移动构造的所有条件,它的自定义成员_name将会调用_name自己的移动构造,若没有移动构造将调用拷贝构造*/
    Person s3 = move(s1);  
    cout << endl;
    
    Person s4;
    //移动赋值

    /*与移动构造同理,若定义了_name的移动赋值将调用它移动赋值,若没有定义将调用它的拷贝赋值*/
    s4 = move(s2); 
    cout << endl;
    return 0;
}

        若在以上Person类中实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,将会输出不一样的结果。

class Person
{

    //.......
    ~Person() {    }  //在以上代码上加上了析构函数

    //.......
};

//main函数都一样

        这里可观察到默认移动语义比其它默认函数的条件更为苛刻,需满足两个条件:1,没有自己实现移动语义。2,没有自己实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。

        第一个条件好理解,第二个条件就不知所措了。其实这里之所以要这样设计还是跟析构函数 、拷贝构造、拷贝赋值重载三者的作用有关。系统默认生成的它们三个函数都是浅拷贝,当我们自己实现时说明需要深拷贝,这时说明我们需要控制对象的拷贝复制和移动行为,因此这里就不再默认生成,若有需要可自己实现。

1-2,强制生成关键字

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

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

#include "String.h" //头文件
class Person
{
public:
    Person(const char* name = "", int age = 0)
        :_name(name) , _age(age)
    {    }

    ~Person() {    }

    //上面自己编写了析构函数将不会有默认移动语义,这里强制生成默认移动语义
    Person(Person&& p) = default;
    Person& operator=(Person && p) = default;

    //上面强制生成了移动语义将不会有默认拷贝和默认赋值,这里强制生成默认拷贝和默认赋值
    Person(const Person& p) = default; 
    Person& operator=(const Person& p) = default;
private:
    bit::string _name; 
    int _age;
};
int main()
{
    //默认构造
    Person s1;
    cout << endl;

    //拷贝构造
    Person s2 = s1;
    cout << endl;

    //拷贝赋值
    s1 = s2;
    cout << endl;
    
    //移动构造
    Person s3 = move(s1); 
    cout << endl;
    
    //移动赋值
    Person s4;
    s4 = move(s1);
    cout << endl;

    return 0;
}

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

        有些类有时我们可能不希望有个别的默认函数,如拷贝构造、拷贝赋值等。如果想要限制这些默认函数的生成,C++98的做法是自己只声明该函数,且将该函数设置成private(防止别人在类外实现),这样就可做到万无一失,只要调用就会报错。C++11新增关键字delete对此做出处理,只需在该函数声明加上 “=delete” 即可,该语法指示编译器不生成对应函数的默认版本。“=delete”修饰的函数为删除函数。

        下面以禁止Person类进行拷贝和赋值为例。

class Person
{
public:
    Person(const char* name = "", int age = 0)
        :_name(name), _age(age)
    {    }

    ~Person() {    };

    //C++11的做法,delete关键字直接解决
    Person(Person&& p) = delete;
    Person& operator=(Person && p) = delete;
private:
    //以下是C++98的做法,只声明不实现,且放到私有
    //Person(const Person& p);
    //Person& operator=(const Person & p);

    bit::string _name; 
    int _age;
};


二,可变参数模板

2-1,模板参数包

        C++11的新特性可变参数模板能够让我们创建可以接受任意数量和类型的模板包。模板参数包是将传入任意数量和类型的模板参数打包形成的一个整体。相比 C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变参数模版无疑是一个巨大的改进。可变参数模板的知识运用比较广泛,这里不纠结过于复杂的结构,掌握一些基础的可变参数模板特性就足够使用了。

        下面就是一个基本可变参数的函数模板,并尝试使用sizeof来输出参数个数。具体细节和说明如下:

/*Args是一个模板参数包,args是一个函数形参参数包。这里可对比template<class Args> void func(Args args);运用时将传入的模板参数打包,形成Args,然后通过Args参数包中的类型定义具有Args包中模板参数类型的函数形参,将其打包形成args函数参数包*/
template <class... Args> //模板参数包Args,具有0到N个类型的模板参数
void func(Args... args)    //函数参数包args,具有0到N个类型的函数参数
{
    cout << sizeof...(Args) << " ";   //输出模板参数包所包含的参数数量  
    cout << sizeof...(args) << endl; //输出函数参数包所包含的参数数量(它们实际上大小是一样的)
    /*注意: 无法使用sizeof(Args或args),因为都是参数包。参数包的整体使用前面要加上“...”说明*/
}

int main()
{
    func();  //无参形式
    func(1, 1, 2); //同类型参数形式
    func(1, 1.2, "bit", 'a');  //不同类型参数形式
    return 0;
}

        上面的参数Args前面有省略号,表示它是一个参数表,函数形参args同理。它们里面包含了0到N(N>=0)个模版参数,当函数实例化后,以上面func(1, 1.2, "bit", 'a');为例,编译器将会在编译时生成void func<int,double,const char*,char>(其它形式同理)。

        由于参数个数和类型不定,所以使用sizeof输出的是参数的个数。下面的问题是又该如何使用包里面的参数?我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。参数包中的个数可以使用sizeof获取,有些人可能会使用数组的形式使用,但C++11语法并不支持使用args[i]这样的方式获取可变参数。

递归函数方式展开参数包

        C++11使用递归函数方式展开参数包,具体形式类似于以下代码:

#include <iostream>
using namespace std;
//编译时递归返回条件,即递归终止函数
void _func() { cout << endl; }
//注意: 递归返回条件不能使用下面的,因为出现无参函数时将会出错,即出现func()的情况
//template <class T>
//void _func(const T& data) { cout << data << endl; }

/*编译时递归解析,依次拿到参数包args里面的参数传递给T类型的data,直到参数包args为空时调用递归终止函数_func()*/
template <class T, class... Args>
void _func(const T& data, Args ...args)
{
    cout << data << " ";
    _func(args...);  //递归遍历
}

//参数包args中有0-N个参数
template <class ...Args>
void func(Args ...args)
{
    _func(args...); //传入参数包
}


int main()
{
    func(); 
    func(1);
    func(1, 1, 1);
    func(1, 1.2, "bit", 'a'); 
    return 0;
}

        模板在编译阶段实例化,编译器实例化后将会把参数包展开,这里的递归过程可理解为参数的诼渐展开过程,以func(1, 1.2, 'a');为例,展开过程如下:

//编译阶段中,func(1, 1.2, 'a')在主函数传入模板被实例化后,将会推演生成以下类似的代码
void func(int val1, double ch, char s)
{
    _func(val1, ch, s);
}

//诼渐往下抽象递归解包参数,直到遍历到递归终止函数
void _func(const int& data, double ch, char s)
{
    cout << data << " ";
    _func(ch, s);  //往下调用void _func(const double& data, char s)
}

void _func(const double& data, char s)
{
    cout << data << " ";
    _func(s);  //往下调用void _func(const char& data)
}

void _func(const char& data)
{
    cout << data << " ";
    _func(); //递归终止函数
}

逗号表达式展开参数包

        逗号表达式的思想是通过展开表达式来依次拿到参数包里面的参数,与递归不同,系统调用时会生成N个表达式,然后通过这些表达式分别调用对应参数的模板函数,如下:

//模板函数

template <class T>
void _Func(T t)
{
    cout << t << " ";
}
//展开函数
template <class ...Args>
void Func(Args... args)
{
    int arr[] = { (_Func(args), 0)... }; /*使用逗号表达式(PrintArg(args), 0)...展开参数包args中参数,直到参数包为空*/

    cout << endl;
}
int main()
{
    Func(1); 
    Func(1, 'A');
    Func(1, 'A', std::string("sort"));
    return 0;
}

分析:

        运用时,{(_Func(args), 0)...}将会展开成{(_Func(arg1),0), (_Func(arg2), 0), (Func(arg3), 0), ... )},以上面Func(1, 'A')为例,这里会创建{(_Func(1),0), (_Func('A'), 0)},最终会创建一个元素值都为0的数组int arr[sizeof...(Args)]。由于是逗号表达式,在创建数组的过程中会先执行逗号表达式前面的部分_Func(args)打印出参数,也就是说在构造int数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展开参数包。虽然这个数组无用,但是若直接运用{(_Func(args), 0)...}表达式将不会展开,因为没有      系统报错。

参数包的综合运用

        参数包“...”(三个点)的运用情况还有很多。“...”的位置不同,它所对应的情况也有所不同。这方面的逻辑调用分很多情况,但具体的运用理解思路对应的也就以下几种情况。

template <class ...Args> /*“...”放在参数包Args的左边,表示中间有N个参数打包成了Args,运用时将类型参数全部展开*/
void ShowList(Args... args) /*“...”表示参数包中有N个类型,为每个类型在Args中生成一个对应的函数参数,将其打包成args,运用时将形参和类型全部展开*/

运用时为了方便记忆,上面参数包“...”的定义都放在函数参数包和模板参数包的左边

void _func() { cout << endl; }
template <class T, class... Args>
void _func(const T& data, Args ...args)
{
    _func(args...);  /*“...”放在“()”里面args的右边,表示具有N个类型,调用时只将传入的形参全部展开,如上面_func(val1, ch, s)递归展开*/
}

 

template <typename ...Args>
struct A
{
    T _data;
    template<class ...Args>
    A(Args&&... args): _data(forward<Args>(args)...) {} /*构造函数模板参数包的万能引用。“...”在参数包args括号“()”外面的右边,表示具有N个forward<Args>(args)式子,调用时会将其表达式展开,如同上面的逗号表达式*/
};

参数包的调用时,“...”要放在其后面,表示存在N个参数或式子,当“...”直接放在参数包名称的后面时,只是表示参数具有N个,调用时会将参数全部展开,如上面_func(val1, ch, s);当“...”放在表达式的后面时,表示表达式具有N个,调用时会将表达式全部展开,如上面逗号表达式

        模板参数包灵活运用的基本理解如上,其它语法运用基本都是固定语法,如sizeof...(args)。若是出现这一固定搭配可自行查看专门文档,这里不再一一说明。 

        明白了这一原理后这里会发现一个潜在的问题,当传入的参数数量过多,即参数包过大,递归层数过多,系统会支撑不住这么大的开销,发生错误,实际应用中应注意。类似于以下代码:

template<size_t N>
void fun()
{
    cout << N << " ";
    fun<N - 1>();
}

template<>
void fun<1>()
{
    cout << 1 << endl;
}

int main()
{
    fun<15000>();  //传入参数数量过大,递归层数过多,系统支撑不下
    return 0;
}

2-2,STL容器empalce的相关接口

        C++中的emplace是一个常用的技术,主要用于在容器中就地构造元素,而不是先创建一个元素,然后将其复制到容器中。这种方法通常比先创建再复制(或移动)更高效,因为它减少了不必要的临时对象的创建和销毁。

   emplace通常在标准模板库(STL)的容器中看到,如vectorlistmapset等。每个这些容器都可能有一个或多个名为emplace_backemplace_frontemplace等的成员函数。下面我们以list容器中的emplace_backpush_back为例展示。

        其中emplace_back底层的接口是万能引用参数包,push_back底层的接口是const左值引用和右值引用。

示例:

        假设我们有一个vector<string>,并且我们想要添加一个新的字符串。使用传统的push_back插入方法类似于以下:

vector<string> v;

//使用v.push_back("bit");内部形式如同以下

string str = "bit"; //先构造出string的临时对象

v.push_back(str); //然后将此对象移动拷贝到v容器中

        若使用emplace我们可以直接在v中构造字符串,从而避免了不必要的复制(或移动)。

vector<string> vec;
vec.emplace_back("bit");  //直接在容器中构造新的string对象,没有创建临时的string对象

        这里不难发现,若是直接传递对象,emplace将不会做出任何改变。emplace的主要作用是为了减少不必要的拷贝,如同string str("bit")的优化,将拷贝+拷贝构造直接优化成拷贝构造,若是没有不必要的构造,emplace与其它功能相同的函数的使用没有任何区别。还有就是emplace不能使用"{}"(初始化列表)。主要是因为emplace函数不是通过列表初始化来构造对象的,而是使用直接初始化或拷贝初始化,因为多参数要进行隐式类型转换,若进行隐式类型转换就必须要构造出临时对象,与底层底层结构不匹配。当出现多参数时这里可直接使用“()”。

        下面我们使用上面模拟实现的string类来观察这一现象。

#include <list>
#include "String.h"
int main()
{
    list<bit::string> lt1;
    bit::string s1("xxxx");
    lt1.push_back(s1); //调用拷贝构造bit::string在容器中创建string对象
    cout << endl;
    lt1.push_back(move(s1)); //调用移动拷贝bit::string将资源转移到容器string对象中
    cout << "=============================================" << endl;
    //对象传递,没有临时对象的不必要拷贝,emplace_back的调用输出跟push_back一样
    bit::string s2("xxxx");
    lt1.emplace_back(s2);
    cout << endl;
    lt1.emplace_back(move(s2));
    cout << "=============================================" << endl;
    //先将"xxxx"构造成bit::string对象,然后拷贝到lt1容器中
    lt1.push_back("xxxx");
    cout << endl;
    //直接在lt1容器中构造数据是"xxxx"的bit::string对象
    lt1.emplace_back("xxxx");
    cout << "=============================================" << endl;
    list<pair<bit::string, bit::string>> lt2;
    //下面输出两个重复的信息是因为pair是两个指令
    pair<bit::string, bit::string> kv1("xxxx", "yyyy");
    lt2.push_back(kv1);
    lt2.push_back(move(kv1));
    cout << "=============================================" << endl;

    pair<bit::string, bit::string> kv2("xxxx", "yyyy");
    //对象的传递,即便存储类型是pair键值对也是同理,与push_back输出一样
    lt2.emplace_back(kv2);
    lt2.emplace_back(move(kv2));
    cout << "=============================================" << endl;
    lt2.emplace_back("xxxx", "yyyy");
    cout << endl;
    lt2.push_back({ "xxxx", "yyyy" });
    cout << "=============================================" << endl;
    return 0;
}

        通过以上可发现,由于emplace只接受非对象时的改进,而非对象在这方面的运用是构造+移动语义,emplace的改进只是省去了移动语义。移动语义只是移动资源,内部消耗很小,也就是说emplace的改善在实现有移动语义类的情况下效率没有多大提高,但是在没有实现有移动语义类的情况下效率将大大提升,如push_back内部是构造+拷贝构造,emplace_back内部是构造。

class Date
{
public:
    Date(int year, int month, int day)
        :_year(year), _month(month), _day(day)
    {
        cout << "Date(int year, int month, int day)" << endl;
    }
    Date(const Date& d) //拷贝构造,没有移动语义
        :_year(d._year), _month(d._month), _day(d._day)
    {
        cout << "Date(const Date& d)" << endl;
    }
private:
    int _year = 1;
    int _month = 1;
    int _day = 1;
};

int main()
{
    list<Date> lt1;
    lt1.push_back({ 2024,3,30 });
    cout << endl;
    lt1.emplace_back(2024, 3, 30);
    cout << endl << endl;

    //匿名对象。匿名对象也是对象,emplace与push的实现相同
    lt1.push_back(Date(2023, 1, 1));
    cout << endl;
    lt1.emplace_back(Date(2023, 1, 1));
    return 0;
}

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

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

相关文章

stable diffusion WebUi本地安装

一、stable diffusion 介绍 Stable Diffusion是一种先进的文本到图像的生成模型&#xff0c;它可以根据给定的文本输入生成高度逼真的图像。 Stable Diffusion模型因其高效性和灵活性&#xff0c;在AI图像生成领域引起了广泛关注&#xff0c;并在实际应用中展示了其强大的能力…

数据结构深入理解--栈

目录 一、栈的定义 二、栈的实现 2.1 栈的结构 2.2 栈的初始化 2.3 栈的销毁 2.3 栈元素的插入 2.4 栈元素的删除 2.5 栈顶元素获取 2.6 栈元素有效个数获取 2.7 栈是否为空判断 三、代码总览 Stack.h Stack.c 测试代码:test.c 四、例题 例一&#xff1a; 例二&#xff…

【比邻智选】MF871U模组

&#x1f680;搭载国产芯&#xff0c;严苛测试&#xff0c;稳定可靠 &#x1f6e0;️R16特性加持&#xff0c;5G LAN&#xff0c;纳秒级精度 &#x1f310;超低成本&#xff0c;丰富协议&#xff0c;连接无界限

《A data independent approach to generate adversarial patches》论文分享(侵删)

原文链接&#xff1a;A data independent approach to generate adversarial patches | Machine Vision and Applications author{Xingyu Zhou and Zhisong Pan and Yexin Duan and Jin Zhang and Shuaihui Wang}, 一、介绍 在图像识别领域&#xff0c;与数字域中的攻击相比…

ZigBee设备入网流程抓包分析(以飞利浦灯泡为例)

1.第一步&#xff0c;网关打开入网许可&#xff0c;广播Pjoin 2.第二步&#xff0c;设备开始扫网&#xff0c;发送Beacon Request 3.第三步&#xff0c;网关收到Beacon Request请求后&#xff0c;应答Beacon数据帧 4.收到可入网的Beacon帧后&#xff0c;发送关联请求&#xff0…

Linux提权--内核漏洞--web用户提权(脏牛)本地提权(脏管道)

免责声明:本文仅做技术交流与学习... 目录 Linux-内核漏洞Web用户提权-探针&利用-脏牛dcow nmap扫描目标IP及端口 导入脚本,进行探针 通过MSF发现目标机器存在脏牛漏洞 ---上传信息搜集的文件,查找漏洞,利用漏洞,继续上传EXP. --密码改了,再用xshell连一下就行了. …

实物仿真平台设计方案:927-8路GMSL视频注入回灌的自动驾驶半实物仿真平台

8路GMSL视频注入回灌的自动驾驶半实物仿真平台 一、平台介绍 产品基于8路GMSL视频注入回灌的自动驾驶半实物仿真平台旨在提高实验室及研究生院师生在基础软件层开发、计算机视觉和深度学习方面的专业知识学习和实践能力&#xff0c;为师生提供一个稳定软件开发和多精度框…

智慧校园的主要功能是什么

随着信息化的发展&#xff0c;智慧校园的应用已经屡见不鲜。智慧校园是新技术与新科技落地的典型案例。智慧校园完善了校园信息化建设体系&#xff0c;推动了教育水平的提升&#xff0c;以下是智慧校园实现的几个比较典型的功能&#xff1a; 1.数字化办公 毋庸置疑&#xff0…

【定制化】在Android平台实现自定义的程序启动页

特别说明&#xff1a;以下仅适用于Android平台。 实现原理 创建安卓端自定义的Activity禁用UnityPlayerActivity的启动Logo改用自定义Activity 示例效果 参考简单步骤或详细步骤都可实现。 自定义的启动动画&#xff0c;效果如下&#xff1a; 简单步骤 三步操作实现启动动画…

解决wangEditor使用keep-alive缓存后,调用editor.cmd.do()失败

前提&#xff1a;wangeditor版本&#xff1a;4.7.11 vue版本&#xff1a;vue2 问题&#xff1a;在使用wangeditor富文本编辑器时&#xff0c;需求需要通过点击一个按钮&#xff0c;手动插入定义好的内容&#xff0c;所以使用了 editor.cmd.do(insertHTML, ....) 方法新增…

xilinx xdma drive 传输8MB以上数据受限的问题

当传输超过8 MB数据时报错error code1359&#xff0c; #define XDMA_MAX_TRANSFER_SIZE (8UL * 1024UL * 1024UL) 可以修改成&#xff1a; #define XDMA_MAX_TRANSFER_SIZE (80UL * 1024UL * 1024UL) VS2019 WDK环境的搭建 先准备好VS WDK的驱动开发环境。需要下载VS、SD…

【无标获取S4与ECC的具体差异的方法题】

首先我们需要对ECC vs S4的差异这个课题要有一个深刻的理解&#xff0c;这不是一个简单并能准确说清楚的课题。 我们需要结合实际项目的具体情况去回答这个问题&#xff0c;因为这个问题本身是没有标准答案的。 首先要了解SAP本身ERP产品线的发展概况&#xff0c;其次我们要…

LeetCode---396周赛

题目列表 3136. 有效单词 3137. K 周期字符串需要的最少操作次数 3138. 同位字符串连接的最小长度 3139. 使数组中所有元素相等的最小开销 一、有效单词 按照题目要求&#xff0c;统计个数&#xff0c;看是否符合条件即可&#xff0c;代码如下 class Solution { public:b…

conan2 基础入门(03)-使用(msvc为例)

conan2 基础入门(03)-使用(msvc为例) 文章目录 conan2 基础入门(03)-使用(msvc为例)⭐准备生成profile文件预备文件和Code ⭐使用指令预览正确执行结果可能出现的问题 ⭐具体讲解conanconanfile.txt执行 install cmakeCMakeLists.txt生成项目构建 END ⭐准备 在阅读和学习本文…

Springboot集成Netflix-ribbon、Enreka实现负载均衡-12

Netflix Ribbon简介 Netflix Ribbon是Netflix发布的云中间层服务开源项目&#xff0c;主要功能是提供客户端的软件负载均衡算法&#xff0c;将Netflix的中间层服务连接在一起。 具体来说&#xff0c;Ribbon是一个客户端负载均衡器&#xff0c;可以在配置文件中列出所有的服务…

力扣例题(循环队列)

链接 . - 力扣&#xff08;LeetCode&#xff09; 描述 思路 我们使用数组来创建循环队列 数组的大小我们就额外对开辟一块空间 MyCircularQueue(k) 开辟一个结构体&#xff0c;存放队列的相关数据 分别为size,数组指针_a,起始位置head,结束位置tail 注意&#xff1a;我们…

【harbor】harbor的搭建与使用

harbor的搭建与使用 文章目录 harbor的搭建与使用1. harbor的下载2. 创建ssl证书3.harbor的配置3. docker修改4.启动harbor5.使用docker总结 1. harbor的下载 harbor仓库地址&#xff1a;https://github.com/goharbor/harbor harbor主要是go语言写的&#xff0c;但是我们dock…

Docker停止不了

报错信息 意思是&#xff0c;docker.socket可能也会把docker服务启动起来 解决 检查服务状态 systemctl status dockersystemctl is-enabled docker停止docker.socket systemctl stop docker.socket停止docker systemctl stop docker知识扩展 安装了docker后&#xff0c;…

计算机字符集产生的历史与乱码

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

python数据分析——pandas数据结构2

参考资料&#xff1a;活用pandas库 导入基础数据 # 导入库 import pandas as pd # 读取数据集 dfpd.read_csv(r"..\data\scientists.csv") df.head() 1、DataFrame DataFrame是Pandas中最常见的对象。可以把它看作python存储电子表格式数据的方式。Series数据结构…