31 C++11

本节目标

  1. c++11简介
  2. 列表初始化
  3. 变量类型推导
  4. 范围for循环
  5. 新增加容器
  6. 右值
  7. 新的类功能
  8. 可变参数模板

1. c++11简介

在2003年标准委员会提交了一份计数勘误表(简称TC1),使得c++03这个名字已经取代了c++98称为c++11之前的最新的c++标准名称。不过由于c++03(TC1)主要是对c++98标准中的漏洞进行修复,语言的核心部分没有改动,因此人们习惯性的把两个标准合并称为c++98/03标准。从c++0x到c++11,c++标准10年磨一剑,第二个真正意义上的标准姗姗来迟。相对于c++98/03,c++11则带来了数量可观的变化,其中包含了约140个新特性,以及对c++03标准中约600个缺陷的修正,这使得c++11更像是从c++98中孕育出来的新语言。相比较而言,c++11能更好的用于系统开发和库开发、语法更加泛化和简单化,更稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用的比较多,所以要作为一个重点去学习。c++11增加的语法特性篇幅很多,没办法一一讲解
https://en.cppreference.com/w/cpp/11

小故事:
1998年是C++标准委员会成立的第一年,本来计划以后每5年视实际需要更新一次标准,C++国际标准委员会在研究C++03的下一个版本的时候,一开始计划2007年发布,所以最初这个标准叫C++07。但是到06年的时候,官方觉得2007年肯定完不成C++07,而且官方觉得2008年可能也完不成。最后干脆叫C++0x。x的意思是不知道到底能在07还是08还是09年完成。结果2010年的时候也没完成,最后在2011年终于完成了C++标准。所以最终定名为C++11

2. 统一的列表初始化

2.1 {} 初始化

在c++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。比如

struct Point
{int _x;int _y;
};int main()
{int arr1[] = { 1, 2, 3, 4, 5 };int arr2[5] = { 0 };Point p = { 1, 2 };return 0;
}

c++11扩大了用大大括号的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义类型,使用初始化列表时,可添加等号(=),也可不添加

int x1 = 1;
int x2{ 2 };//new也可以列表初始化
int* pa = new int[4] {0};

创建对象u额可以用列表初始化方式调用构造函数初始化,但有本质区别,是先用括号的内容构造一个临时对象,再拷贝构造给初始化对象,会优化为直接构造

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;}
private:int _year;int _month;int _day;
};//创建对象列表初始化
Date d1( 2022, 1 ,1 ); //旧初始化方式
Date d2{ 2022, 1, 2 };
Date d3 = { 2022, 1, 3 };//单参数构造函数支持隐士类型转换
string str = "xxxxx";//证明,这里不能优化,所以转不了,必须加const
const Date& d4 = { 2023, 11 , 5 };

2.2 std::initializer_list

介绍文档
http://www.cplusplus.com/reference/initializer_list/initializer_list/

是什么类型:

 // the type of il is an initializer_list auto il = { 10, 20, 30 };cout << typeid(il).name() << endl;

在这里插入图片描述

Date d1( 2022, 1 ,1 );
vector<int> v1 = { 0, 1, 2, 3, 4 };

可以使用迭代器遍历

initializer_list<int> l2 = { 0, 1, 2, 3, 4 };
initializer_list<int>::iterator it = l2.begin();
while (it != l2.end())
{cout << *it << " ";it++;
}

在这里插入图片描述

这里的v1和d1不一样,上面的是构造的对象,下面是先构造的initializer_list类型,v1的参数个数可以是随意的,d1只能是三个

使用场景
std::initializer_list一般是作为构造函数的参数,c++11对stl中的不少容器增加了它作为参数的构造函数,这样初始化容器就方便多了。也可以作为operator=的参数,就可以用大括号赋值

在这里插入图片描述
让vector也支持{}初始化和赋值

vector(std::initializer_list<T> lt)
{Reserve(lt.size());for (auto& e : lt){PushBack(e);}
}
vector<T>& operator=(initializer_list<T> l) {vector<T> tmp(l);std::swap(_start, tmp._start);std::swap(_finish, tmp._finish);std::swap(_endofstorage, tmp._endofstorage);return *this;}

当参数的个数和构造函数匹配时会识别为对象,不匹配时会认为是initializer_list类型

3. 声明

c++11通了多种简化声明的方式,尤其是在使用模板时

3.1 auto

c++98中auto是一个存储类型的说明符,表明变了是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。c++11废除auto原来用法,将其用于实现自动类型判断,这样要求必须显示初始化,让比那一期将定义对象的类型设置为初始化值的类型
typeid可以获得变量的类型字符串

int i = 10;
auto p = i;
auto pf = strcpy;cout << typeid(p).name() << endl;
cout << typeid(pf).name() << endl;

在这里插入图片描述

3.2 decltype

上面可以推导类型,但推导的类型不能用来创建变量,如果想根据某个变量类型推导并创建变量,可以用decltype,将变量的类型声明为表达式指定的类型

double y = 2.2;
decltype(y) ret = 3.3;cout << typeid(ret).name() << endl;

3.3 nullptr

由于c++中NULL被定义为字面量0,这样就可能带来一些问题,0既指针常量,又表示整形常量。所以出于清晰和安全的角度考虑,c++11新增了nullptr,表示空指针

#ifndef NULL#ifdef __cplusplus#define NULL    0#else#define NULL    ((void *)0)#endif#endif

4.范围for循环

5. stl一些变化

圈起来的是几个新容器,但是实际最有用的是unordered_map和unordered_set。
在这里插入图片描述
array和内置数组相比,越界访问会报错
容器的新方法
增加的新方法都用的比较少。比如提供了cbegin和cedn方法返回const迭代器等待,但意义不大,begin和end也可以返回const迭代器,属于锦上添花的操作
插入接口函数增加了右值版本
http://www.cplusplus.com/reference/vector/vector/emplace_back/
意义在哪,说能提高效率,如何提高的

在这里插入图片描述

6. 右值引用和移动语义

6.1 左值引用和右值引用

传统c++语法就有引用,c++11新增了右值引用特性,无论是左值还是右值引用,都是给对象取别名

左值是一个表示数据的表达式(变量名或解引用的指针),可以获取它的地址,可以赋值,左值可以出现在赋值符号左边,右值不能出现在赋值符号左边。定义时const修饰后的左值,不能赋值,但可以取地址。左值引用就是给左值的引用,取别名

int* p = new int(0);
int b = 1;
const int c = 2;//以下都是左值引用
int*& rp = p;
int& rb = b;
const int& rc = c;
int& pvalue = *p;

什么是右值,什么是右值引用
右值也是一个表示数据的表达式,如:字面常量、表达式返回值、函数返回值(这个不能是左值引用返回)等待,右值可以出现在赋值符号的右边,不能出现在左边,右值不能取地址。右值引用就是对右值的引用,给右值取别名

double x = 1.1, y = 2.2;
//以下是常见的右值
10;
x + y;
fmin(x, y);//以下几个都是右值引用
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);//编译会报错,error c2106, "=":左操作数必须为左值
10 = 1;
x + y = 1;
fmin(x, y) = 1;

6.2 左值引用和右值引用比较

左值引用总结:
1.左值引用只能引用左值,不能引用右值,但是const左值引用既可以引用左值,也可以引用右值
2.右值引用不能引用左值,move的可以

//左值引用不能给右值取别名,const左值可以
int& r1 = 10;
const int& r2 = 10;
//右值引用不能给左值取别名,move可以
int i = 10;
int&& rr3 = i;
int&& rr4 = move(i);

6.3 左值引用使用场景和意义

左值做参和返回值都可以提高效率,减少了拷贝

#pragma once
#include <string>
#include <iostream>
#include <assert.h>class string
{
public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){//cout << "string(char* str)" << endl;_str = new char[_capacity + 1];strcpy(_str, str);}// s1.swap(s2)void swap(string& s){::std::swap(_str, s._str);::std::swap(_size, s._size);::std::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s):_str(nullptr){std::cout << "string(const string& s) -- 深拷贝" << std::endl;string tmp(s._str);swap(tmp);}// 赋值重载string& operator=(const string& s){std::cout << "string& operator=(string s) -- 深拷贝" << std::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
};

左值引用的短板
当函数返回对象是一个局部变量,除了函数作用域就不存在了,不能使用左值引用返回,只能传值返回。例如:string to_string(int value)函数中可以看到,这里只能传值返回,传值返回会导致至少一次拷贝构造(如果旧一点的编译器可能是两次拷贝构造)

string to_string(int value)
{string ret;while (value){int x = value % 10;value /= 10;ret += '0' + x;}std::reverse(ret.begin(), ret.end());return ret;
}

旧编译器会产生两次拷贝构造,返回的ret对象拷贝一次,赋值的时候也会调用一次赋值重载。既然ret已经是一个要销毁的对象了,多次拷贝就会造成资源的浪费。下面第一个是连续的构造和拷贝构造都可以优化一次拷贝构造
在这里插入图片描述
下面这个无法优化
在这里插入图片描述
右值引用和移动语义解决上述问题
右值可以分为:
1.纯右值,内置类型右值
2.将亡值,自定义的右值
移动构造本质是将参数右值的资源窃取过来,占为己有,不做深拷贝,叫它移动构造,就是窃取别人的资源构造自己。所以可以实现拷贝和赋值的移动版本
编译器会选择最匹配的调用,to_string返回的是右值,如果既有拷贝又有右值,就会匹配移动构造

//移动拷贝
string(string&& s)
{std::cout << "string(string&& s) -- 移动语义" << std::endl;swap(s);
}
//移动赋值
string& operator=(string&& s)
{std::cout << "string& operator=(string s) -- 移动语义" << std::endl;swap(s);return *this;
}

在这里插入图片描述
s和ret的字符串是同一个

运行后调用了一次移动构造和移动赋值,因为如果用一个已经存在的对象接收,编译器没办法优化,to_string函数中先用str生成构造一个临时对象,但是可以看到,编译器把str识别成了右值,调用了移动构造,然后把临时对象作为to_string函数调用的 返回值赋值给ret1,调用的移动赋值

stl容器都增加了移动构造和移动赋值

string s1("hello");
string s2 = s1;
string s3 = std::move(s1);

6.4 右值引用左值及一些深入的使用场景

按照语法,右值引用只能引用右值,但右值引用一定不能引用左值吗?有些场景下,需要用右值去引用左值实现移动语义。当需要右值引用左值时,可以通过move函数将左值转化为右值,c++11中,std::move()函数位于头文件中,该函数名字具有迷惑性,并不搬移任何东西,唯一的功能是将一个左值强制转换为右值使用,实现移动语义

template<class _Ty>inline typename remove_reference<_Ty>::type&& move(_Ty&& _Arg) _NOEXCEPT{// forward _Arg as movablereturn ((typename remove_reference<_Ty>::type&&)_Arg);}
//move会改为右值,s1的资源置空,转移给了s3
string s1("hello");
string s2 = s1;
string s3 = std::move(s1);
//move的返回值是右值,并不改变变量本身
string s4 = s1;

stl容器也加入了右值引用版本
在这里插入图片描述

std::list<string> l1;
string s1 = "hello";
//左值
l1.push_back(s1);
//右值
l1.push_back(to_string(1234));运行结果:
// string(const string& s) -- 深拷贝
// string(string&& s) -- 移动语义

在这里插入图片描述

修改list
在前面的list中加入右值插入的版本,先把pushback函数加入右值,这时还是会有深拷贝
右值被右值引用以后得属性是左值,编译器设计因为右值引用需要被修改

在这里插入图片描述
这里x传入下层又变为了左值,需要传给inert的右值版本move后的,insert函数里创建节点也需要再次move

__list_node(T&& x): _prev(nullptr), _next(nullptr), _data(std::move(x))
{}void push_back(T&& x)
{Insert(end(), std::move(x));
}void Insert(iterator pos, T&& x)
{node* new_node = new node(std::move(x));//记录前后节点node* pre = pos.node_iterator->_prev;node* cur = pos.node_iterator;//连接pre->_next = new_node;new_node->_prev = pre;new_node->_next = cur;cur->_prev = new_node;}list<string> l1;
string s1 = "hello";
l1.push_back(s1);
l1.push_back(to_string(1234));

在这里插入图片描述

6.5 完美转发

模板中的&&万能引用

void fun(int& x) { std::cout << "左值引用" << std::endl; };
void fun(const int& x) { std::cout << "const 左值引用" << std::endl; };
void fun(int&& x) { std::cout << "右值引用" << std::endl; };
void fun(const int&& x) { std::cout << "const 右值引用" << std::endl; };// 模板的&&不是右值引用,是万能引用,既能接收左值,也能右值
// 引用类型的唯一作用是限制了接收的类型,后续使用都退化成了左值
// 想要保持左值和右值的属性,要使用完美转发
template <typename T>
void PerfectForward(T&& t)
{fun(t);
};PerfectForward(10);  //右值
int a;
PerfectForward(a);  //左值
PerfectForward(std::move(a));  //右值
const int b = 8;
PerfectForward(b);  //左值
PerfectForward(std::move(b));  //右值

上面的t后续都退化成了左值,想要保持传入的属性,就要加入std::forward保留属性

fun(std::forward<T>(t));

使用场景
容器的插入等可以直接使用完美转发,代替左值和右值两个版本

7. 新的类功能

默认成员函数
原来c++类中,有6个默认成员函数:
1.构造函数
2.析构函数
3.拷贝构造函数
4.拷贝赋值重载
5.取地址重载
6.const取地址重载

最后重要的是前4个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的
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即可,指示编译器不生成对应函数的默认版本,修饰的为删除函数

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);}

继承和多态中的final与override关键字
final修饰类或虚函数,表示不可被继承或重写。override检测虚函数是否完成重写

8. 可变参数模板

c++11的新特性可变参数模板能够创建可以接收可变参数的函数模板和类模板,相比c++98/03,类模板和函数模板中只能含固定数量的模板参数,可变模板参数无疑是一个巨大的改进。然而由于可变模板参数比较抽象,使用起来需要一定的技巧,所以这块还是比较晦涩的。掌握一些基础的可变模板参数特性就可以了

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

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

递归函数展开参数包

// 递归终止函数
void _ShowList()
{std::cout << std::endl;
}
//展开函数
template <class T, class ...Args>
void _ShowList(const T& value, Args ...args)
{std::cout << value << " ";_ShowList(args...);
}template <class ...Args>
void ShowList(Args ...args)
{_ShowList(args...);
}int main()
{ShowList(1, 2, 'x');ShowList(1, 2, 3.5);return 0;
}

逗号表达式展开
这种方式展开参数包,不需要通过递归终止函数,是直接在expand函数体中展开的,printarg不是一个递归终止函数,指示一个处理参数包每一个参数的函数。这种就地展开参数包的方式实现的关键是逗号表达式。逗号表达式会按顺序执行,返回最后一个

expand函数中的逗号表达式也(printarg(args),0),也是按照这个执行顺序,限制性printarg(args),在得到逗号表达式的结果0,同时还用到了c++11的另外一个特性–初始化列表,通过初始化列表来初始化一个变长数组,{(printarg(args), 0}将会展开成(printarg(arg1), 0),(printarg(arg2), 0), (printarg(arg3), 0), etc…),最终会创建一个元素值都为0的数组Int arr[sizeof…(args)]。由于是逗号表达式,在创建数组的过程中回显执行逗号表达式前面的部分printarg(args)打印出参数,也就是说在构造int数组的过程中就将参数包展开了,这个参数的目的纯粹是为了在数组构造的过程展开参数包

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

stl容器中empalce相关接口

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

emplace系列接口,支持模板的可变参数,并且万能引用。那么相对insert优势在哪里

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;}

emplace是由模板参数包直接传入参数,不会拷贝一个临时对象。而pushback需要拷贝构造或移动构造。移动构造的消耗也不是很高

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;}

在这里插入图片描述

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

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

相关文章

JAVA学习笔记DAY12——MySQL基础

文章目录 数据库概述为什么要使用数据库相关概念关系型和非关系型RDBMS非RDBMS - NoSQL MySQL图形化管理工具Navicat SQL前后端环境nginx 反向代理MD5加密 数据库概述 为什么要使用数据库 持久化&#xff1a;指把数据保存到可掉电存储设备中&#xff0c;一般指将内存中的数据…

详解归一化、标准化、正则化以及batch normalization

文章目录 what(是什么)where&#xff08;用在哪&#xff09;How&#xff08;如何用&&原理&#xff09;归一化实现方式原理示例说明 标准化实现方式原理示例说明 正则化实现方式原理作用 Batch Normalizationpytorch中的batch normalization原理BN的作用 归一化、标准化…

代理设计模式和装饰器设计模式的区别

代理设计模式: 作用:为目标(原始对象)增加功能(额外功能,拓展功能) 三种经典应用场景: 1&#xff1a;给原始对象增加额外功能(spring添加事务,Mybatis通过代理实现缓存功能等等) 2&#xff1a;远程代理&#xff08;网络通信&#xff0c;输出传输&#xff08;RPC&#xff0c;D…

【TB作品】智能台灯,ATMEGA16单片机,Proteus仿真

智能台灯 1 adc检测光强光敏电阻 显示电压 2 光强太高 也就是高于临界值 就关闭小灯 3 光强太低 也就是低于临界值 就打开小灯 3 按键修改临界值 显示 实验报告&#xff1a;基于ATMEGA16单片机的智能台灯设计与Proteus仿真 1. 实验背景 智能台灯是一种能够根据环境光强自动调…

代码随想录第40天|动态规划

完全背包 完全背包物品可以无限使用 01背包核心代码 01背包中的二维dp数组的两个for遍历可颠倒, 而一维dp数组的一定先遍历物品再遍历背包容量状态转移方程(背包容量一定为递减) 完全背包核心代码 (只在完全背包中一维dp数组嵌套顺序可颠倒, 实际题目需要确定遍历顺序) 状…

【高考志愿】建筑学

目录 一、专业介绍 1.1 专业定义 1.2 专业培养目标 1.3 核心课程 二、就业方向和前景 2.1 就业方向 2.2 专业前景 三、报考注意 四、行业趋势与未来展望 五、建筑学专业排名 一、专业介绍 1.1 专业定义 建筑学&#xff0c;这一充满艺术与科技魅力的学科&#xff0c;…

天线 有源 无源 参数

无源测试驻波比VSWR/回波损耗(Return Loss)≤2效率≥50%输入阻抗50R10%增益天线方向图3D场强图方向性 有源测试 OTA 传导测试&#xff1a;发射功率传导测试&#xff1a;接收灵敏度总辐射功率TRP(Total Radiated Power)≥发射功率减3dB总接收灵敏度TIS&#xff08;Total Isotrop…

JDBC1(JDBC相关类与接口 ​连接mysql数据库​ 测试 不同数据库厂商实现-MySQL和Oracle)

目录 一、JDBC 1. JDBC相关类与接口 1.1 DriverManager 1.2 Connection 1.3 Statement 4.ResultSet 2. JDBC工作原理 二、连接mysql数据库 1. 导入jar包 2. 使用DriverManager加载驱动类 3. Connection接口 4. Statement接口 5. ResultSet接口 ​编辑 6. 关闭并…

显卡简介

显卡是计算机系统中一个重要的组成部分&#xff0c;它负责处理图形和视频输出。显卡的性能直接影响到计算机的图形处理能力&#xff0c;因此在游戏、视频编辑、3D渲染等需要高性能图形处理的应用中&#xff0c;显卡的选择至关重要。本文将从显卡的基本概念、性能指标、市场现状…

【Node.JS】入门

文章目录 Node.js的入门涉及对其基本概念、特点、安装、以及基本使用方法的了解。以下是对Node.js入门的详细介绍&#xff1a; 一、Node.js基本概念和特点 定义&#xff1a;Node.js是一个基于Chrome V8引擎的JavaScript运行环境&#xff0c;它使得JavaScript能够运行在服务器…

【鸿蒙学习笔记】基础组件Progress:进度条组件

官方文档&#xff1a;Progress 目录标题 作用最全属性迭代追加进度赋值风格样式 作用 进度条组件 最全属性迭代追加 Progress({ value: 20, total: 100, type: ProgressType.Linear }).color(Color.Green)// 颜色.width(200)// 大小.height(50)// 高度.value(50)// 进度可更…

视频转音频:怎样提取视频中的音频?6个提取音频的小技巧(建议收藏)

怎样提取视频中的音频&#xff1f;当我们想从视频中提取出声音时&#xff0c;通常会遇到很多问题。无论是想单独提取出视频里的音频&#xff0c;还是把它转成方便储存或者分享的音频格式&#xff0c;这都会涉及到视频转音频的一个需求。因此&#xff0c;在这篇指南里&#xff0…

Spring Cloud - 项目搭建

1、新建maven项目 新建maven项目&#xff0c;该项目为主项目 1、新建maven项目 2、设置项目类型 3、选择项目原型 4、设置参数 5、等着完成 2、设置项目信息 1、右键&#xff0c;项目属性 2、设置jdk版本 3、选择jdk17 4、修改编译版本 5、右键项目&#xff0c;选择maven->u…

【吊打面试官系列-MyBatis面试题】模糊查询 like 语句该怎么写?

大家好&#xff0c;我是锋哥。今天分享关于 【模糊查询 like 语句该怎么写?】面试题&#xff0c;希望对大家有帮助&#xff1b; 模糊查询 like 语句该怎么写? 第 1 种&#xff1a;在 Java 代码中添加 sql 通配符。 string wildcardname “%smi%”; list<name> names …

CDH安装和配置流程

这份文件是一份关于CDH&#xff08;Clouderas Distribution Including Apache Hadoop&#xff09;安装的详细手册&#xff0c;主要内容包括以下几个部分&#xff1a; 1. **前言**&#xff1a; - CDH是基于Apache Hadoop的发行版&#xff0c;由Cloudera公司开发。 - 相比…

技术派全局异常处理

前言 全局的异常处理是Java后端不可或缺的一部分&#xff0c;可以提高代码的健壮性和可维护性。 在我们的开发中&#xff0c;总是难免会碰到一些未经处理的异常&#xff0c;假如没有做全局异常处理&#xff0c;那么我们返回给用户的信息应该是不友好的&#xff0c;很抽象的&am…

【一篇文章带你搞懂--拉链表!!!拉链表的原理是什么!】

前言&#xff1a; &#x1f49e;&#x1f49e;大家好&#xff0c;我是书生♡&#xff0c;今天主要和大家分享一下拉链表的原理以及使用,希望对大家有所帮助。 大家可以关注我下方的链接更多优质文章供学习参考。 &#x1f49e;&#x1f49e;代码是你的画笔&#xff0c;创新是你…

深入解析:WebKit的JavaScript引擎与V8引擎的比较研究

在现代Web开发中&#xff0c;JavaScript引擎是浏览器的核心组件之一&#xff0c;它们负责解析和执行JavaScript代码。WebKit和V8是两个非常著名的JavaScript引擎&#xff0c;分别被用于不同的浏览器和环境中。WebKit的JavaScript引擎最初是Nitro&#xff0c;后来被JavaScriptCo…

【超简单-Java设计模式1】设计模式的定义、分类及七大设计原则

引言 Java设计模式从入门到精通-设计模式的定义、设计模式分类及七大设计原则 设计模式简介 在软件开发中&#xff0c;设计模式是解决常见设计问题的最佳实践。它们为开发者提供了一种通用的解决方案&#xff0c;使得代码更加灵活、可复用和可维护。在Java编程语言中&#x…

Flink 运行时架构

Flink 运行时的组件 作业管理器&#xff08;JobManager&#xff09;资源管理器&#xff08;ResourceManager&#xff09;任务管理器&#xff08;TaskManager&#xff09;分发器&#xff08;Dispatch&#xff09; JobManager 控制一个应用程序执行的主进程&#xff0c;也就是说…