【C++进阶之路】C++11(上)

文章目录

  • 一、列表初始化
    • 1.{}
    • 2.initializer_list
  • 二、声明
    • 1.auto
    • 2.deltype
  • 三、右值与左值
    • 1.基本概念
    • 2.应用场景
      • 1.左值引用
      • 2.右值引用
      • 3.完美转发
      • 4.万能引用
  • 四、新增默认成员函数
  • 五、lambda表达式
    • 1.基本语法
      • 1.1捕捉列表
      • 1.2参数列表
      • 1.3返回类型
      • 1.4函数体
    • 2.底层原理
  • 总结

一、列表初始化

1.{}

  • 内置类型
	int a1{ 1 };int a2 = { 2 };
  • 自定义类型
	class Point{public:Point(int a,int b):_a(a),_b(b){}private:int _a;int _b;};int main(){//数组int arr1[]{ 1,2,3 };//列表初始化int arr2[] = { 1,2 };//数组初始化。//自定义类型初始化Point p1{ 1,2 };//这里是显示调用构造函数Point p2 = { 1,2 };//本质上是C++11支持多参数的隐式类型转换,编译器优化为直接构造。//验证:在构造函数前面+explicit 语法会报错。const Point& p4 = { 1,2 };//多参数的隐式类型转换,生成一个匿名对象其具有常性,不可被修改。int* ptr1 = new int[] {1, 2, 3};Point* ptr2 = new Point[]{{1,2},{1,3}};//外面的一层为数组,里面的为列表初始化。return 0;}

总结:

  • 一切即可用{}进行初始化。
  • {}支持多参数的隐式类型转换,也用于单参数的隐式类型转换。
  • 对于自定义类型初始化不加 = 直接跟 {}——显示调用构造函数。
  • 对于加 = 再跟{}初始化——隐式类型转换+编译器的优化 - > 调用构造函数(构造函数前加explicit验证)。

2.initializer_list

  • initializer_list是编译器特殊支持的内置容器,不可单独实现,是一个常量数组。
    在这里插入图片描述
    说明:这是库里initializer_list的所有接口。
	int main(){const char* str = "abcd";//常量区字符串。返回的是首字母的地址。int arr[] = {1,2,3,4};//直接进行赋值。initializer_list<int> lt;//此处是默认构造函数进行初始化initializer_list<int> lt = { 1,2,3,4 };//常量区数组,此处存于栈区。//此处是编译器直接进行识别进行初始化,可以理解是直接内存赋值,//与arr数组的赋值雷同。return 0;}
  • 常见用途:作为构造函数的参数进行初始化/赋值
    在这里插入图片描述

  • 注意

	vector<int> v = { 1,2,3,4 };//隐式类型转换 + 编译器优化,调用initializer_list的构造函数进行初始化。Point p = { 1,2 };//隐式类型转换+编译器优化,调用相应的构造函数进行初始化。
  • 接口实现
	vector(initializer_list<T>& lt){reserve(lt.size());for(auto e : lt){push_back(e);}//或者直接调用相应的迭代器区间进行初始化,不过得用现代写法再进行交换。//注意:构造函数是不能够显示进行调用的!//vector tmp(lt.begin(), lt.end());//swap(tmp);}

其它的接口类似。

二、声明

1.auto

详见:初始C++语法

举例:

int main()
{auto p1 = malloc;auto p2 = Point(1, 2);auto p3 = { 1,2,3 };auto p4 = "123";auto p5 = 1;auto str = string();cout << typeid(p1).name() << endl;cout << typeid(p2).name() << endl;cout << typeid(p3).name() << endl;cout << typeid(p4).name() << endl;cout << typeid(p5).name() << endl;cout << typeid(str).name() << endl;cout << endl;//typeid().name的返回值的类型为char const* ,即指向的内容不可被修改。cout << typeid(typeid(p1).name()).name() << endl;return 0;
}

补充:auto是在编译期间进行推导得出类型的,且右边必须有可推导的值,因此不可对类型进行声明。

2.deltype

  • 可得出类型,且可以当做类型名进行使用。
template<class T>
class Func
{T _a;
};
int main()
{auto p1 = malloc;decltype(p1) p2;Func<decltype(p2)> p3;return 0;}
  • 总结
  1. 推导出的类型可以进行声明。
  2. 推导出的类型可以当做函数参数。

用途:一些迭代器类型的声明,以及复杂指针的声明。

三、右值与左值

1.基本概念

  • 右值:不能取地址,一般把内置类型的右值叫做纯右值,自定义类型的右值叫将亡值。
  • 左值:能取地址。
  • 左值引用:对左值起别名。
  • 右值引用:对右值起别名。
  • 引用:不管左值引用还是右值引用,都是对值起别名。

  • 常见右值
int add(int x, int y)
{return x + y;
}
int main()
{int a = 0;int b = 0;//常见的右值//表达式a + b;//函数的返回值add(a, b);//常量10;//验证:不能取地址。//cout << &10 << &add(a, b) << &(a + b) << endl;return 0;
}

特殊的左值:

int main()
{//特殊的左值。"abc";//返回的是首字符a的地址。const char* p = "abc";p[1];//这也是左值。return 0;
}

  • 左值引用
int main()
{int a = 0;int b = 0;//左值引用直接对左值取别名int& ref1 = a;int& ref2 = b;//const 左值引用可以引用右值const int& ref3 = a + b;const int& ref4 = 10;return 0;
}

拓展:

	const int& ref3 = 10;int* ptr = (int*)&ref3;*ptr = 100;cout << &ref3 << endl;cout << ref3 << endl;//const左值引用引用右值,开空间存储右值,对const左值引用可以取地址,因此const左值引用为左值。

  • 右值引用
int main()
{int a = 0, b = 0;int&& ref5 = 10;int&& ref6 =  a + b;return 0;
}
  • 拓展
int main()
{int a = 0;int&& ref = move(a);//cout << &move(a) << endl;//右值引用可以引用move之后的左值,move之后的结果为右值。int&& ref1 = 10;cout << &ref1 << endl;//右值引用能取地址,说明右值引用为左值,对10开空间进行拷贝。ref1++;//右值引用可以进行修改,更说明其为左值。int&& ref2 = forward<T>(ref1);//叫做完美转发,即保持右值属性,此处了解即可。int&& ref3 = move(ref2);//将左值ref2转换为右值。//说明:右值引用也可作为右值,需要进行处理。return 0;
}

2.应用场景

1.左值引用

  1. 作为函数参数
  2. 作为返回值,前提是引用的值在出作用域不被销毁。
  • 起到的作用都是减少拷贝。

缺陷:当返回一个局部的自定义的很大的对象时,只能用传值返回,不能用传引用返回。

说明:写一份string方便查看细节:

namespace MY_STL
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){_str = new char[_capacity + 1];strcpy(_str, str);}// s1.swap(s2)void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s):_str(nullptr){cout << "string(const string& s) -- 深拷贝" << endl;_size = s._size;_capacity = s._capacity;_str = new char[_capacity + 1];strcpy(_str, s._str);}// 赋值重载string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷贝" << endl;string tmp(s);swap(tmp);return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}//string operator+=(char ch)string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity; // 不包含最后做标识的\0};
}
  • 举例:
MY_STL::string func()
{MY_STL::string str("xxxxxxxxxxxxxxxxxx");return str;
}int main()
{//1.连续的拷贝构造进行优化。MY_STL::string str = func();//2.不连续的拷贝构造,进行逐步拷贝。MY_STL::string str1;str1 = func();return 0;
}

1.图解
在这里插入图片描述

说明:编译器优化的结果是——在return之前就调用拷贝构造进行构造,因为return后就调用析构函数对空间进行销毁了。

2.图解
在这里插入图片描述

运行结果图解:

在这里插入图片描述

2.右值引用

 前面我们举了左值引用的缺陷,那右值引用是来弥补这一个缺陷的,如何弥补呢?接着继续分析。

第一个问题:

void func(const int& a)
{cout << "func(const int& a)" << endl;
}
void func(int && a)
{cout << "func(int && a)" << endl;
}
int main()
{int a = 0;int b = 0;func(a);func(a + b);return 0;
}

运行结果如何?构成重载么?如果构成,那是否存在歧义?

  • 运行结果
    在这里插入图片描述
    结果很显然构成重载,且不存在歧义,函数调用会走最匹配的。

接着用上面的例子:

MY_STL::string func()
{MY_STL::string str("xxxxxxxxxxxxxxxxxx");return str;
}int main()
{MY_STL::string str1;str1 = func();return 0;
}

 根据前面的思路分析,返回str时发生了一次拷贝构造,生成了匿名对象(右值),且其声明周期只在这一行,那么我们是否可以利用这一特性写出个右值引用版本的赋值呢?那关键就在于这是右值且声明周期只在当前一行,因此我们可以直接换资源,我们顺走匿名对象的资源,之后让匿名对象带走不用的资源(析构函数)

根据这样我们写出来的赋值重载就是这样的:

void swap(string& s)
{::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);
}
string& operator=(string&& s)
{cout << "string& operator=(string&& s) -- 移动赋值" << endl;swap(s);return *this;
}
  • 补充:右值引用可以被修改,且默认识别为左值。

我们再来运行一下:

在这里插入图片描述

 对比之前,这里就优化了一次深拷贝,在一定程度上提升了效率。


继续使用上面的例子再进行分析:

MY_STL::string func()
{MY_STL::string str("xxxxxxxxxxxxxxxxxx");return str;
}int main()
{//1.连续的拷贝构造进行优化。MY_STL::string str = func();return 0;
}

 按理来说这里本该为拷贝构造生成匿名对象,再用匿名对象拷贝构造对象,但编译器看不惯连续的拷贝构造,因此直接优化为了一次拷贝构造,这里如何用右值进行优化呢?编译器直接将返回的str特殊处理成右值,然后用识别之后的右值,进行拷贝构造,那如何写拷贝构造呢?其实跟赋值一样。

string(string&& s):_str(nullptr)
{cout << "string(string&& s)——移动拷贝" << endl;swap(s);
}

运行结果:
在这里插入图片描述
看结果,确实是编译器对返回值进行了优化,那为啥要进行这样的优化呢?第一点博主认为这样代码具有可移植性,第二点是不用再对之前的代码进行修改即在返回值处修改成move(str)。

此时我们再看之前的那个例子:

MY_STL::string func()
{MY_STL::string str("xxxxxxxxxxxxxxxxxx");return str;
}int main()
{//1.连续的拷贝构造进行优化。MY_STL::string str = func();return 0;
}

再看运行结果:

在这里插入图片描述
这里编译器也是做了优化的,对返回值直接识别成右值,直接将两次拷贝构造,优化为两次移动构造,这样处理对一些很大的自定义的值返回,带来了效率的极大的提升!并且写起来也变的更加轻松。


除此之外为了更好更深刻的理解问题,博主举一个例子:

int main()
{const string& str = string("xxxxx");string("xxxxx").push_back('a');return 0;
}

这会引出来几个问题:

  1. 匿名对象具有常属性不可以被修改,但是这里却可以调用接口被修改。
  2. 匿名对象左值引用只能用const左值引用,之后就不可调用接口修改,因为是this指针被const修饰,这一点很容易理解。
  3. 匿名对象右值引用可以被修改,且修改的是匿名对象本身,而对内置类型则是修改其拷贝。

 可以看出匿名对象确实是右值或者说是常量(const左值引用),但也确实可以被修改,其原因就在于这是C++委员会开的绿灯,其目的就在于通过右值引用修改其内部成员而完成资源的转换,进而实现移动赋值(语义)和移动拷贝(语义),而对内置类型的右值进行引用则是开空间存储(消耗不大),因此我们可以把右值引用的最终目的就是为了减少资源的拷贝进行提升效率。

总结一下关键:

  1. 对于内置类型的右值可以认为是常量绝对不可以被修改,而自定义类型的右值也认为是常量,但为了达到目的,开了绿灯因此可以被修改。
  2. 右值引用对于内置类型开空间拷贝,对自定义类型直接取别名,且右值引用之后的别名可以进行修改,且被识别为左值(方便修改),进而方便转资源,提升效率。

 那右值引用是否具有右值属性呢?答案是肯定的。在有些情况下,是需要保持右值属性的。

比如:当我们在vector中push_back一个string。
在这里插入图片描述

int main()
{MY_STL::vector < MY_STL::string> v;v.push_back("xxxxx");//尾插一个匿名对象的string。return 0;
}

因此push_back可优化成这样:

在这里插入图片描述
 但是又涉及一个问题,虽然这里是右值引用,但这里会被识别为左值,进而调用赋值重载跟上面的一样,这要怎么办呢?也就是如何保持其右值的属性呢?

3.完美转发

 完美转发就是来解决上面的问题,右值引用保持其右值的属性,左值引用保持其左值的属性。
 那么最终的push_back就是:

void push_back(value_type&& val)
{if (_finish == _end_of_storage){size_t new_capacity = size() == 0 ? 4 : \capacity() * 2;//扩容reserve(new_capacity);}*(_finish++) = forward<value_type>(val);//这样就叫做完美转发。
}
  • 这样就能在插入数据时进一步优化效率。

4.万能引用

 最后再简单的提及一个比较省力的小语法。

void Fun(int& x) 
{ cout << "左值引用" << endl;
}
void Fun(const int& x) 
{ cout << "const 左值引用" << endl; 
}
void Fun(int&& x) 
{cout << "右值引用" << endl; 
}
void Fun(const int&& x) 
{cout << "const 右值引用" << endl; 
}
template<typename T>
void PerfectForward(T&& t)
{Fun(forward<T>(t));
}
int main()
{PerfectForward(10); // 右值int a;PerfectForward(a); // 左值PerfectForward(move(a)); // 右值const int b = 8;PerfectForward(b); // const 左值PerfectForward(move(b)); // const 右值return 0;
}

运行结果:
在这里插入图片描述
图解:
在这里插入图片描述

四、新增默认成员函数

 C++11新增了两个默认成员函数,不过需要具备以下条件才会自动生成。

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

例:

class Person
{
public:Person(const char* name = "", int age = 0):_name(name), _age(age){}
private:MY_STL::string _name;int _age;
};
int main()
{Person s2;Person s3("xxxxx",10);s3 = move(s2);return 0;
}

 分析为什么要这样设计?关键在于默认生成的移动构造函数对内置类型是只拷贝字节的。也就是说我们在类内部定义一个指针,用个指针指向堆空间的资源,默认生成只完成浅拷贝,而不完成资源的交换!并且因为默认生成的析构函数不会对内置类型处理,因此还会造成内存泄漏 , 这就要求我们自己实现析构函数,但是写了析构函数就不会生成默认的移动构造了,这就又反过来要求我们自己写移动构造交换资源。
 这样编译器选择生成的目的在于只要你写了实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个就会默认认为有需要深拷贝的内置成员,因此不会生成只会对内置类型完成按字节拷贝的默认移动构造。

  • 对于移动赋值重载同理。

五、lambda表达式

 lambda其实就是一个对象,有类似函数,可以在语句里面定义即使用。

在之前我们还学过函数指针和仿函数与之相同。

1.基本语法

基本语法:

[capture-list] (parameters) mutable -> return-type { statement}
//捕捉列表          参数      控制属性     返回类型        实现语句

先举个例子看看如何使用:

struct Goods
{string _name; // 名字double _price; // 价格int _evaluate; // 评价Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};
int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3 }, { "菠萝", 1.5, 4 } };auto CompareNameLess = [](const Goods& x1, const Goods& x2)->bool { return x1._name < x2._name; };auto CompareNameGreater = [](const Goods& x1, const Goods& x2)->bool{ return x1._name > x2._name; };sort(v.begin(), v.end(), CompareNameLess);sort(v.begin(), v.end(), CompareNameGreater);//也可以直接这样写:sort(v.begin(), v.end(), [](const Goods& x1, const Goods& x2)->bool { return x1._name < x2._name; });sort(v.begin(), v.end(), [](const Goods& x1, const Goods& x2)->bool { return x1._name > x2._name; });//箭头以及后面的类型可省略,编译器会自动进行推导。sort(v.begin(), v.end(), [](const Goods& x1, const Goods& x2){ return x1._name > x2._name; });return 0;
}

 这样使用其实有一定的好处,就是避免了有人使用仿函数或者函数指针时,定义的类或者函数名字不规范而导致代码的可读性极差,还要回去找定义才能进行理解, 花费的时间成本很大。

再来谈谈其具体的几个部分:

1.1捕捉列表

 捕捉列表其实是用所在局部域的变量,因为其实lambda就是一个类的匿名对象,其定义的内容在底层是一个函数,又因为函数与函数之间的域是独立的,不可互相使用变量只能通过传参的形式或者使用全局变量的方式间接使用,这里的捕捉列表就是为了间接使用不同域之间的变量而诞生的。

  • 捕捉值
  1. 捕捉指定值。
int main()
{int x = 1, y = 2;auto tmp = [x, y]() { //x = 0;并且此处是const的左值,不可被修改。return x + y; };return 0;
}

 这里的x与y进行值捕捉是加const的,因此不可被修改,如果非要修改呢?

 很简单:在()后加 mutable。

这样就可以被修改了:

int main()
{int x = 1, y = 2;auto tmp = [x, y]() mutable{ x = 0;return x + y; };return 0;
}

说明: mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。

class Person {
public:void modify()const{//const函数//比如int float等类型,其值不可修改。//指针的值不可被修改,内容是可以被修改的。//引用底层是指针,其指向的内容是可以被修改的。//static静态对象不受this指针的约束也可以被修改。//age++;//name = "aaaa";nums++;*name;ref = 0;_id = 0;}private:int age;int& ref = age;char* name;mutable int nums;static int _id;
};
int Person::_id = 0;
  1. 捕捉所有值(如果有this则this也算进去)
int main()
{int x = 1, y = 2;auto tmp = [=]() mutable{ x = 0;return x + y; };return 0;
}

  • 捕捉引用
  1. 捕捉指定引用
int main()
{int x = 1,y = 2;auto tmp = [&x,&y](){ return x + y; };return 0;
}
  1. 捕获所有引用(如果有this包含this)
int main()
{int x = 1,y = 2;auto tmp = [&](){ return x + y; };return 0;
}

注意

  1. 捕捉列表不允许变量重复传递,否则就会导致编译错误,比如x以值的形式捕捉过一回就不能用引用再进行捕获。
  2. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割。比如:[=, &a, &b]:以引用传递的方式捕捉变量a和b。
  3. 在全局定义lambda表达式,其捕捉列表必须为空。
  4. lambda只能捕捉其所在局部作用域的变量。
  5. lambda之间不能能进行赋值,因为类型不同。

1.2参数列表

  • 与普通的函数列表一样。

1.3返回类型

  • 通常可通过实现体进行推导,所以一般情况下可以省略。

1.4函数体

  • 写函数的实现功能。

2.底层原理

  1. 类型
int main()
{auto tmp = []() {};cout << typeid(tmp).name() << endl;return 0;
}

运行结果:
在这里插入图片描述

可以看出其是一个类,至于这么长,博主猜测是采用了哈希函数的那套方法进行映射出了一段关键码,由此产生不同的类,这也间接说明了不同lambda表达式的类型各不相同。

  1. 反汇编
struct tmp1
{bool operator()(){return 0;}
};
int main()
{auto tmp = []()->bool { return 0; };tmp();tmp1()();return 0;
}

反汇编截图:
在这里插入图片描述

  • 因此:底层是仿函数。

总结

 今天的分享到此就结束了,下篇文章再见咯,如果有所帮助,不妨点个赞鼓励一下吧!

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

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

相关文章

【Redis】五大数据类型 、历史概述、nosql分类

文章目录 NoSql概述NoSql年代缓存 Memcached MySQL垂直拆分&#xff08;读写分离&#xff09;分库分表水平拆分Mysql集群最近为什么要用 NoSqlNoSql的四大分类 Redis测试性能 五大数据类型keyStringSetHashZset 前言&#xff1a;本文为看狂神视频记录的笔记 NoSql概述 NoSql年…

nodejs开发环境搭建

Nodejs是一个开源的、跨平台JavaScript运行时环境&#xff0c;其使用V8引擎对JavaScript脚本执行解释&#xff0c;在前后端分离的应用架构设计中&#xff0c;其既能支持web页面服务应用的开发、也能支持后端接口服务应用的开发&#xff0c;类似于Java语言的J2EE运行时环境&…

stm32 - GPIO

stm32 - GPIO GPIO结构图GPIO原理图输入上拉/下拉/浮空施密特触发器片上外设 输出推挽/开漏/关闭输出方式 GPIO88种模式复用输出 GPIO寄存器端口配置寄存器_CRL端口输入数据寄存器_IDR端口输出数据寄存器_ODR端口位设置/清除寄存器_BSRR端口位清除寄存器_BRR端口配置锁定寄存器…

ElementUI之CUD+表单验证

&#x1f3ac; 艳艳耶✌️&#xff1a;个人主页 &#x1f525; 个人专栏 &#xff1a;《Spring与Mybatis集成整合》《Vue.js使用》 ⛺️ 生活的理想&#xff0c;为了不断更新自己 ! 1.前言 首先&#xff0c;Vue是一种流行的JavaScript框架&#xff0c;它提供了一种简洁易用的方…

UGUI交互组件Button

一.初识Button对象 从菜单中创建Button对象&#xff0c;Button的文本由子节点Text对象显示&#xff0c;Button对象的组件除了基础组件外&#xff0c;还有Image用来显示Button常规态的图片&#xff0c;还有Button组件用来控制点击过渡效果和点击事件的响应。 二.Button组件的属…

POJ 3109 Inner Vertices 离散化+树状数组

一、题目大意 围棋棋盘&#xff0c;如果某个坐标上下左右的四个方向都存在棋子&#xff0c;那么ans1&#xff0c;根据输入的棋子数量&#xff0c;求出ans的数量。 二、解题思路 题目中有说到如果程序不会结束&#xff0c;那么输出-1&#xff0c;这其实是无源之水&#xff0c…

【vue3】shallowReactive与shallowRef;readonly与shallowReadonly;toRaw与markRaw

假期第六篇&#xff0c;对于基础的知识点&#xff0c;我感觉自己还是很薄弱的。 趁着假期&#xff0c;再去复习一遍 1、shallowReactive与shallowRef shallowReactive&#xff1a;只处理对象最外层属性的响应式&#xff08;浅响应式&#xff09; shallowRef&#xff1a;只处理…

WSL2安装历程

WLS2安装 1、系统检查 安装WSL2必须运行 Windows 10 版本 2004 及更高版本&#xff08;内部版本 19041 及更高版本&#xff09;或 Windows 11。 查看 Windows 版本及内部版本号&#xff0c;选择 Win R&#xff0c;然后键入winver。 2、家庭版升级企业版 下载HEU_KMS_Activ…

【Python】函数(function)和方法(method)的区别

这里先说结论&#xff0c;为了满足心急的小伙伴&#xff1a;method与function的最大区别就是参数有无进行绑定。 自定义类Test&#xff1a; 首先先来一个自定义类&#xff1a; class Test:def Func_normal(arg):print(Func_normal:,arg)staticmethoddef Func_static(arg):pri…

Lua多脚本执行

--全局变量 a 1 b "123"for i 1,2 doc "Holens" endprint(c) print("*************************************1")--本地变量&#xff08;局部变量&#xff09; for i 1,2 dolocal d "Holens2"print(d) end print(d)function F1( ..…

短期风速预测|LSTM|ELM|批处理(matlab代码)

目录 1 主要内容 LSTM-长短时记忆 ELM-极限学习机 2 部分代码 3 程序结果 4 程序链接 1 主要内容 该程序是预测类的基础性代码&#xff0c;程序对河北某地区的气象数据进行详细统计&#xff0c;程序最终得到pm2.5的预测结果&#xff0c;通过更改数据很容易得到风速预测结…

【计算机组成原理】读书笔记第五期:通过汇编语言了解程序的实际构成

目录 写在开头 汇编语言和本地代码的关系 汇编语言的源代码 伪指令 汇编的基本语法 常见的汇编指令 mov push和pop 函数的使用机制 函数的调用 函数参数的传递与返回值 全局变量 局部变量 程序的流程控制 循环语句 条件分支 通过汇编语言了解程序运行方式的必…

德国自动驾驶卡车公司【Fernride】完成1900万美元A轮融资

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;总部位于德国沃尔夫斯堡的自动驾驶卡车公司【Fernride】今日宣布已完成1900万美元A轮融资&#xff0c;本轮融资完成后Fernride的融资金额已经达到了达到5000万美元。 本轮融资由Deep Tech and Cli…

推荐算法——Apriori算法原理

0、前言&#xff1a; 首先名字别读错&#xff1a;an pu ruo ao rui 【拼音发音】Apriori是一种推荐算法推荐系统&#xff1a;从海量数据中&#xff0c;帮助用户进行信息的过滤和选择。主要推荐方法有&#xff1a;基于内容的推荐、协同过滤推荐、基于关联规则的推荐、基于知识的…

多线程(pthread库)

POSIX线程库 引言 前面我们提到了Linux中并无真正意义上的线程 从OS角度来看&#xff0c;这意味着它并不会提供直接创建线程的系统调用&#xff0c;它最多给我们提供创建轻量级进程LWP的接口 但是从用户的角度来看&#xff0c;用户只认识线程啊&#xff01; 因此&#xff0c;…

wxWidgets(1):在Ubuntu 环境中搭建wxWidgets 库环境,安装库和CodeBlocks的IDE,可以运行demo界面了,继续学习中

1&#xff0c;选择使用 wxWidgets 框架 选择这个主要是因为完全的开源&#xff0c;不想折腾 Qt的库&#xff0c;而且打包的文件比较大。 网络上面有很多的对比&#xff0c;而且使用QT的人比较多。 但是我觉得wxwidgets 更加偏向 c 语法本身&#xff0c;也有助学习C。 没有太多…

【算法分析与设计】回溯法(上)

目录 一、学习要点1.1 回溯法1.2 问题的解空间1.3 0-1背包问题的解空间1.4 旅行售货员问题的解空间1.5 生成问题状态的基本方法 二、回溯法的基本思想三、回溯算法的适用条件四、递归回溯五、迭代回溯六、子集树与排列树七、装载问题八、批处理作业调度问题 一、学习要点 理解回…

Kotlin前置检测判断check,require,requireNotNull

Kotlin前置检测判断check&#xff0c;require&#xff0c;requireNotNull &#xff08;1&#xff09;check fun main(args: Array<String>) {val b falsecheck(b) {println("check $b")}println("end") } check监测到值非真时候&#xff0c;抛出一…

【数据结构与算法】通过双向链表和HashMap实现LRU缓存 详解

这个双向链表采用的是有伪头节点和伪尾节点的 与上一篇文章中单链表的实现不同&#xff0c;区别于在实例化这个链表时就初始化了的伪头节点和伪尾节点&#xff0c;并相互指向&#xff0c;在第一次添加节点时&#xff0c;不需要再考虑空指针指向问题了。 /*** 通过链表与HashMa…

Python 无废话-基础知识元组Tuple详讲

“元组 Tuple”是一个有序、不可变的序列集合&#xff0c;元组的元素可以包含任意类型的数据&#xff0c;如整数、浮点数、字符串等&#xff0c;用()表示&#xff0c;如下示例&#xff1a; 元组特征 1) 元组中的各个元素&#xff0c;可以具有不相同的数据类型&#xff0c;如 T…