C++ 11 【可变参数模板】【lambda】

💓博主CSDN主页:麻辣韭菜💓

⏩专栏分类:C++修炼之路

🚚代码仓库:C++高阶🚚

🌹关注我🫵带你学习更多C++知识
  🔝🔝

目录

 

前言

 一、新的类功能 

1.1默认成员函数——移动构造、移动赋值

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

1.3const延长生命周期的问题

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

1.5 其他新功能

缺省值

 委托构造

二、可变参数模板 

利用递归函数展开

逗号表达式展开参数包

三、Lambda 

lambda表达式

 lambda表达式语法

 1.捕捉列表

函数对象与lambda表达式 


 

前言

上篇重点讲解了右值引用,本篇的可变参数模板Lambda也是11里面非常有用的。如果学会这两个以后编程会感觉非常的爽。废话不多说直接开始!!!

 一、新的类功能 

1.1默认成员函数——移动构造、移动赋值

在C++11后,类又新增了两个默认成员函数:移动构造移动赋值

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

  • 如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中 的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内 置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋 值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造 完全类似)
  • 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。
#include "String.h"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& operator=(const Person& p){if(this != &p){_name = p._name;_age = p._age;}return *this;}*//*~Person(){}*/private:gx::string _name;int _age;
};
int main()
{Person s1;Person s2 = s1;Person s3 = move(s1);Person s4;s4 = move(s2);return 0;
}

如果把上面代码的拷贝构造 赋值重载、析构的任意一个代码注释取消注释就会得到下面结果

为什么我们一但自己写了默认成员函数编译器就不会自己生成移动构造、赋值? 

 如果我们实现了 析构、拷贝构造、赋值重载,就证明当前的类中涉及到了 动态内存管理,是需要自己进行 深拷贝 的,编译器无能为力,移动语义 也应该根据自己的实际场景进行设计,所以编译器就没有自动生成

 那如果有些场景就需要我们自己写拷贝构造、析构、赋值重载这些函数那怎么办?

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

Person(Person&& p) = default;
Person& operator=(Person&& p) = default;

C++11后STL中所有容器都增加了移动构造和移动赋值

插入系列的函数也同样增加了右值的版本。

 其他容器详情请看官网cplusplus.com 

1.3const延长生命周期的问题

插入函数之所以会延长生命周期

当您创建一个临时对象并将其作为参数传递给函数时,这个临时对象的生命周期通常只在表达式中有效。一旦表达式结束,临时对象就会被销毁。但是,如果这个临时对象被传递给一个需要更长时间使用它的函数,比如一个需要对对象进行修改的函数,那么就需要延长这个临时对象的生命周期。

在C++中,如果一个函数的参数是一个const类型,这意味着函数不会修改这个对象。但是,如果这个参数是通过引用传递的,那么即使它是const,它仍然需要在函数调用期间保持有效,以便函数可以访问它。这就是所谓的生命周期延长。

既然可以延长对象生命周期那是不是也可以像这样?下图这样返回的对象加const

从结果来看显然是不可以的。出现了野引用的问题。 所以说引用也不是安全的。

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

如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明补丁
已,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete
可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。

 

在person类中我们在拷贝构造函数后面加 =delete 就无法再使用这个这个函数。

注意:delete这个关键字只对默认成员函数有效

那什么样的类是不希望其他人来调用的它的默认成员函数?

比如IO流

 

每个IO流对象的缓冲区都是不一样的,随意拷贝都会造成资源混乱。

1.5 其他新功能

在C++98中,类中的内置类型是不对初始化的。而在C++11中出现了缺省参数 可以给类的成员给缺省值。

缺省值

 没有缺省值我们得到_a的值是随机值。

给定缺省值 1

 委托构造

什么是委托构造? 简单来说就是一个构造函数可以复用其他构造函数

class Person
{
public:Person(const char* name, int age):_name(name), _age(age){}Person(const char* name):Person(name, 18) // 委托构造{}private:gx::string _name; // 自定义类型int _age = 1;		   // 内置类型
};int main()
{Person s1("张三");return 0;
}

 

这个委托构造了解一下就行了,说白了还是要调用构造函数。 

二、可变参数模板 

相比C++98的模板参数,C++11模板参数变成了不是固定的,可以接受任意个类型。和printf函数的可变参数列表是类似的。只是这里的模板参数变成了类型。

下面就是一个基本可变参数的函数模板 

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

如果要知道参数包中个数怎么解决?

template<class ...Args>
void ShowList(Args...args)
{cout << sizeof...(args) << endl;
}
int main()
{ShowList(1, 'x');
}

 如何解析出参数包里面的值?

由于语法不支持使用args[i]这样方式获取可变参数,所以我们的用一些奇招来一一获取参数包的值。

利用递归函数展开

void ShowList()
{cout << endl;
}
template <class T ,class ...Args>
void ShowList(const T& val, Args ...args)
{cout << __FUNCTION__ << "(" << sizeof...(args) << ")" << endl;cout << val << endl;ShowList(args...); //语法规定...必须在后面
}
int main()
{ShowList(1, 'A', std::string("sort"));return 0;
}

 上面结果确实调用3次ShowList这个函数再加上无参ShowList。

能不能不用模板参数T? 我就想直接用可变模板参数包?可以 。直接再套一层。

void _ShowList()
{cout << endl;
}
template <class T, class ...Args>
void _ShowList(const T& val, Args... args)
{cout << __FUNCTION__ << "(" << sizeof...(args) << ")" << endl;cout << val  << endl;_ShowList(args...);
}
template <class ...Args>
void ShowList(Args... args)
{_ShowList(args...);
}int main()
{ShowList(1, 'A', std::string("sort"));return 0;
}

逗号表达式展开参数包

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"));return 0;
}

在上篇的初始化列表 我们知道C++11在arr这个数组创建时,会初始化 列表里面的内容。实现的关键是逗号表达式。我们知道逗号表达式会按顺序执行逗号前面的表达式。 

编译器通过解析变成下面表达式

{(printarg(args), 0)...}将会展开成((printarg(arg1),0), (printarg(arg2),0), (printarg(arg3),0), etc... )

为什么要写出成 (Print(args), 0) 的形式?
这是一个逗号表达式,目的是让整个式子最终返回 0,用于初始化 arr 数组

 如果不想加0也是可以的。

 可变参数模板应用场景又是什么?线程

我们知道C语言中回调函数的传参。C的方案是用的void*   而C++11的线程库用可变参数模板对void* 这个指针进行封装。通过可变参数模板,就可以快乐的传递任何参数。剩下的事交给编译器来干。

可变参数包还可以用于优化STL容器中插入函数。

以容器list为例:

int main()
{list<gx::string> lt;gx::string s1("1111");lt.push_back(s1);lt.emplace_back(s1);return 0;
}

 

对于是深拷贝的类是没区别的。push_back和emplace_back二者没什么区别。

我们在来看看浅拷贝的类有没有影响

#include "Date.h"
int main()
{/*list<gx::string> lt;gx::string s1("1111");lt.push_back(s1);lt.emplace_back(s1);*/list<Date> lt2;Date d1(2024, 5, 20);Date d2(2024, 5, 21);lt2.push_back(d1);lt2.emplace_back(d2);return 0;}

也是没有差别。

那如果是 d1\d2是右值那?

也是没区别

但是是下面这种就有区别了

emplace_back直接就是构造。这是因为可变参数包在参数传递的过程中,参数包不会展开。直到构造函数才展开。其实这里可以理解成(2023,5,28)它不是一个匿名对象,在参数包的眼里它实际是3个整型。

再比如 下面这个。

有了可变参数包。编译器直接识别为const char* 的字符串。而不是一个匿名对象。 

结论:无脑用emplace_back就行。

三、Lambda 

在C++11之前,我们如果要对数据进行排序怎么做?用std::sort。

#include <algorithm>
#include <functional>
int main()
{int array[] = { 4,1,8,5,3,7,0,9,2,6 };// 默认按照小于比较,排出来结果是升序std::sort(array, array + sizeof(array) / sizeof(array[0]));// 如果需要降序,需要改变元素的比较规则std::sort(array, array + sizeof(array) / sizeof(array[0]), greater<int>());return 0;
}

如果我们要排序的是自定义类型那就需要用到仿函数。

struct Goods
{string _name;  // 名字double _price; // 价格int _evaluate; // 评价Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};
struct ComparePriceLess
{bool operator()(const Goods& gl, const Goods& gr){{return gl._price < gr._price;}	}
};
struct ComparePriceGreater
{bool operator()(const Goods& gl, const Goods& gr){return gl._price > gr._price;}
};
int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };sort(v.begin(), v.end(), ComparePriceLess());sort(v.begin(), v.end(), ComparePriceGreater());
}

 生活中商品太多了难道每一种商品的排序都要写相应的仿函数,是不是有点太麻烦?,假设我们不以价格来进行排序。现在要求按水果的名字排序,是不是又要重写一个仿函数?

有没有一种办法一劳永逸?在C++11推出了lambda 

lambda表达式

int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠萝", 1.5, 4 } };sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._price < g2._price; });sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._price > g2._price; });sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._evaluate < g2._evaluate; });sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._evaluate > g2._evaluate; });
}

 

上述代码就是使用C++11中的lambda表达式来解决,可以看出lambda表达式实际是一个匿名函

数。

 lambda表达式语法

lambda 表达式书写格式: [capture-list] (parameters) mutable -> return-type { statement
}
1. lambda 表达式各部分说明
  • [capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]
判断接下来的代码是否为 lambda 函数 捕捉列表能够捕捉上下文中的变量供 lambda
函数使用
  • (parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以
连同 () 一起省略
  • mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量
性。使用该修饰符时,参数列表不可省略 ( 即使参数为空 )
  • ->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回
值时此部分可省略。 返回值类型明确情况下,也可省略,由编译器对返回类型进行推
  • {statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获
到的变量。
注意:
lambda 函数定义中, 参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为
。因此 C++11 最简单的lambda函数为 []{} ; lambda 函数不能做任何事情。

我们先来一个简单的lambda语法

	[](int x, int y)->int {return x + y; };

 如果函数体的语句较多我们也是可以这样写代码的

	[](int x, int y)->int {return x + y; };

我们如果要调用这个函数对象太长了,我们可以加 auto 

	auto add1 = [](int x, int y)->int {return x + y; };cout << add1(1, 2) << endl;

最简单的lambda表达式

//最简单的lambda表达式 该lambda表达式没有任何意义[] {};

 1.捕捉列表

写一个交换函数。

​int x = 1, y = 0;auto swap1 = [](int& rx, int& ry){int tmp = rx;rx = ry;ry = tmp;};swap1(x, y);cout << x << " " << y << endl;

参数列表是可以省略的

省略参数时,我们就要用捕捉列表

 

这时我们可以在参数列表后面加入关键字,mutable//异变 但是没什么用

 

 还是没有交换

上面的这种捕捉方式叫做传值捕捉,传值具有常性,不能修改 这时我们需要用到引用捕捉

这里的引用捕捉就很坑,不注意看还以为是取地址!! 

 

如果参数太多怎么办?难道要在捕捉列表中一个一个的捕捉吗?当然不用。我们直接全部引用捕捉

//全部引用捕捉
auto swap2 = [&]() {int tmp = x;x = y;y = tmp;};

当然还有其他的捕捉方式

 //混合捕捉auto func1 = [&x, y](){//...};//全部传值捕捉auto func3 = [=](){//...};//全部引用捕捉,x传值捕捉auto func4 = [&, x](){//...};

 这时我们就可以用lambad来创建线程

int main()
{int n1, n2;cin >> n1 >> n2;thread t1([n1]( int num){for (int i = 0; i < n1; i++){cout << "线程:" << num << " " << i << endl;}cout << endl;},1);thread t2([n2](int num){for (int i = 0; i < n2; i++){cout << "线程:" << num << " " << i << endl;}cout << endl;}, 2);t1.join();t2.join();return 0;
}

如果要m个线程分别打印n次如何操作? 这里我们可以利用vector 把每个线程放进vector这个容器中。

#include <vector>
int main()
{int m, n;cin >> m >> n;vector<int> arr;arr.push_back(m);arr.push_back(n);vector<thread> vthds(m);for (int i = 0; i < arr[0]; i++){vthds[i] = thread([i,arr](){for (int j = 0; j< arr[1]; j++){cout << "线程:" << i << " " << j << endl;}cout << endl;});}for (auto& t : vthds){t.join();}return 0;
}

 当然这个打印会错乱,那是因为没有加锁导致线程串行。关于锁的问题我们在后序线程库在详细讲解。

lambda 能不能相互赋值?
void (*PF)();
int main()
{auto f1 = []{cout << "hello world" << endl; };auto f2 = []{cout << "hello world" << endl; };//f1 = f2;   // 编译失败--->提示找不到operator=()// 允许使用一个lambda表达式拷贝构造一个新的副本auto f3(f2);f3();// 可以将lambda表达式赋值给相同类型的函数指针PF = f2;PF();return 0;
}

总结: 

捕捉列表描述了上下文中那些数据可以被 lambda 使用 ,以及 使用的方式传值还是传引用
[var] :表示值传递方式捕捉变量 var
[=] :表示值传递方式捕获所有父作用域中的变量 ( 包括 this)
[&var] :表示引用传递捕捉变量 var
[&] :表示引用传递捕捉所有父作用域中的变量 ( 包括 this)
[this] :表示值传递方式捕捉当前的 this 指针
注意:
a. 父作用域指包含 lambda 函数的语句块
b. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割
比如: [=, &a, &b] :以引用传递的方式捕捉变量 a b ,值传递方式捕捉其他所有变量
[& a, this] :值传递方式捕捉变量 a this ,引用方式捕捉其他变量
c. 捕捉列表不允许变量重复传递,否则就会导致编译错误
比如: [=, a] = 已经以值传递方式捕捉了所有变量,捕捉 a 重复

d. 在块作用域以外的 lambda 函数捕捉列表必须为空
e. 在块作用域中的 lambda函数能捕捉父作用域中局部变量。
f. lambda 表达式之间不能相互赋值 ,即使看起来类型相同

函数对象与lambda表达式 

函数对象,又称为仿函数,即可以想函数一样使用的对象,就是在类中重载了 operator() 运算符的
类对象。

 lambda的大小是多大?

要清楚这个问题我们需要通过汇编

先搞一个函数对象和lambda的代码

class Rate
{
public:Rate(double rate) : _rate(rate){}double operator()(double money, int year){return money * _rate * year;}private:double _rate;
};
int main()
{// 函数对象double rate = 0.49;Rate r1(rate);r1(10000, 2);// lambdaauto r2 = [=](double monty, int year)->double {return monty * rate * year; };r2(10000, 2);auto f1 = [] {cout << "hello world" << endl; };auto f2 = [] {cout << "hello world" << endl; };//f1 = f2;return 0;
}

 

f1 和 f2通过汇编我们发现他们两个类名不同,类名不同怎么相互赋值?

这时候我们就能回答大小为什么是1了

在C++中,sizeof运算符用来确定一个类型或对象在内存中的大小。对于一个lambda表达式,sizeof返回的是这个lambda表达式对象在内存中占用的大小。

在x86 32位架构上,指针通常是4字节大小。因此,如果你的lambda表达式没有捕获任何局部变量或外部变量(或者只捕获了通过引用捕获的变量),那么lambda表达式的大小很可能是1字节,这是因为:

  1. Lambda表达式可能被编译器优化为一个很小的函数对象,它只包含一个指向其代码的指针。
  2. 在某些编译器实现中,lambda表达式可能被优化为一个空的结构体,其中只包含一个指向其代码的指针,因此sizeof返回1,表示空结构体的大小。

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

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

相关文章

【数据分析基础】实验三 文件操作、数组与矩阵运算

一&#xff0e;实验目的 掌握上下文管理语句with的使用方法。掌握文本文件的操作方法。了解os、os.path模块的使用。掌握扩展库Python-docx、openpyxl的安装与操作word、Excel文件内容的方法。熟练掌握numpy数组相关运算和简单应用。熟练使用numpy创建矩阵&#xff0c;熟悉常用…

新技术前沿-2024-构建个人知识库和小语言模型

OllamaWebUIAnythingLLM&#xff0c;构建安全可靠的个人/企业知识库 1 技术路线一 1.1 搭建本地大模型Ollama 1.2 搭建用户界面open WebUI 使用Docker Desktop Open-webui。它可以快速基于Ollama构筑本地UI。 如果没有科学上网&#xff0c;很可能会拉不动&#xff0c;可以试…

linux网络服务“PXE网络批量装机和Kickstart全自动化安装”

PXE网络批量装机 pxe自动装机&#xff1a; 服务端和客户端 pxe c/s 模式&#xff1a;允许客户端通过网络从远程服务器&#xff08;服务端&#xff09;下载引导镜像&#xff0c;加载安装文件&#xff0c;实现自动化安装操作系统。 无人值守 :安装选项不需要人为干预&#xf…

FlexJavaFramwork

FlexJavaFramwork架构

【高校科研前沿】广西大学博士生冯德东为一作在Habitat Int发文:区域乡村性与贫困治理变化的时空格局及相关效应——以滇桂黔石漠化地区为例

1.文章简介 论文名称&#xff1a;Spatio-temporal patterns and correlation effects of regional rurality and poverty governance change: A case study of the rocky desertification area of Yunnan-Guangxi-Guizhou, China&#xff08;区域乡村性与贫困治理变化的时空格…

从零开始搭建Electron项目(二)之例程解析

本专栏&#xff0c;前面学习了怎么下载例程并运行。 这里解析例程的构成 从零开始搭建Electron项目之运行例程-CSDN博客文章浏览阅读22次。最好的学习方式就是&#xff1a;给一段能够运行的代码示例。本文给出了例程资源&#xff0c;以及运行的步骤。在国内开发electron有一点特…

【吊打面试官系列-Mysql面试题】MySQL_fetch_array 和 MySQL_fetch_object 的区别是什么 ?

大家好&#xff0c;我是锋哥。今天分享关于 【MySQL_fetch_array 和 MySQL_fetch_object 的区别是什么 &#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; MySQL_fetch_array 和 MySQL_fetch_object 的区别是什么 &#xff1f; 以下是 MySQL_fetch_array 和 MySQL…

让AI做2024新高考1卷数学最后一题:AI智商横向对比!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

【Python】成功解决SyntaxError: invalid syntax

【Python】成功解决SyntaxError: invalid syntax 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通本硕&am…

探索OrangePi AIpro:单板计算机的深度体验之旅

准备阶段&#xff1a;环境与资料 在开始我们的探索之旅前&#xff0c;确保您已准备好以下装备&#xff1a; OrangePi AIpro&#xff1a;我们的主角&#xff0c;一台功能强大的单板计算机。Windows 10笔记本电脑&#xff1a;作为我们的辅助工具&#xff0c;用于管理和测试。路…

SSM民宿在线预订平台的设计与实现-计算机毕业设计源码44449

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对民宿在线预订平台等问题&#xff0c;对民宿…

力扣74. 搜索二维矩阵

给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a;每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示…

1.nginx介绍

介绍 是一个高性能的http和反向代理服务器。 特点 占用内存少&#xff0c;并发能力强。 nginx专为性能优化而开发&#xff0c;性能是其最重要的考量&#xff0c;实现上非常注重效率&#xff0c;能经受高负载的考验&#xff0c;有报告表明能支持高达50,000个并发连接数。 基…

游戏服务器工程实践一:百万级同时在线的全区全服游戏

我应该有资格写这篇文章&#xff0c;因为亲手设计过可以支撑百万级同时在线的全区全服类型的游戏服务器架构。 若干年前我在某公司任职时&#xff0c;参与研发过一款休闲类型的游戏&#xff0c;由 penguin 厂独代。研发的时候&#xff0c;p 厂要求我们的游戏服务器要能支撑百万…

Vue学习|Vue快速入门、常用指令、生命周期、Ajax、Axios

什么是Vue? Vue 是一套前端框架&#xff0c;免除原生JavaScript中的DOM操作&#xff0c;简化书写 基于MVVM(Model-View-ViewModel)思想&#xff0c;实现数据的双向绑定&#xff0c;将编程的关注点放在数据上。官网:https://v2.cn.vuejs.org/ Vue快速入门 打开页面&#xff0…

MySQL事务,视图,用户管理学习笔记【事务概念 | 事务隔离级别 | 设置级别 | 视图 | 用户管理】

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;MySQL之旅_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我们一起努力&#xff0c;一起成长&#xff01; 目录 一&#xff0c;事务初…

面试杂谈之clickhouse

clickhouse 之前定时任务读取binlog 批量同步clickhouse kafka 批量给clickhouse灌数据 clickhouse列式数据库&#xff0c;运行时创建库表&#xff0c;加载数据查询数据压缩&#xff0c;磁盘存储&#xff0c;向量化引擎&#xff0c;利用CPU多核并行处理缺少完整的update/del…

【SQL】牛客网SQL非技术入门40道代码|练习记录

跟着刷题&#xff1a;是橘长不是局长哦_哔哩哔哩_bilibili 6查询学校是北大的学生信息 select device_id, university from user_profile where university 北京大学 7查找年龄大于24岁的用户信息 select device_id, gender, age, university from user_profile where age…

开源低代码平台技术为数字化转型赋能!

实现数字化转型升级是很多企业未来的发展趋势&#xff0c;也是企业获得更多发展商机的途径。如何进行数字化转型&#xff1f;如何实现流程化办公&#xff1f;这些都是摆在客户面前的实际问题&#xff0c;借助于开源低代码平台技术的优势特点&#xff0c;可以轻松助力企业降低开…

设计模式-装饰器模式(结构型)

装饰器模式 装饰器模式是一种结构模式&#xff0c;通过装饰器模式可以在不改变原有类结构的情况下向一个新对象添加新功能&#xff0c;是现有类的包装。 图解 角色 抽象组件&#xff1a;定义组件的抽象方法具体组件&#xff1a;实现组件的抽象方法抽象装饰器&#xff1a;实现…