读书笔记:《Effective Modern C++(C++14)》

Effective Modern C++(C++14)

GitHub - CnTransGroup/EffectiveModernCppChinese: 《Effective Modern C++》- 完成翻译

Deducing Types

  1. 模版类型推导
    1. 引用,const,volatile被忽略
    2. 数组名和函数名退化为指针
    3. 通用引用:
      1. 左值保持引用和const
      2. 右值忽略引用和const
  2. auto 类型推导:
    1. {} 初始化被auto推导为std::initializer_list
    2. 模版推导无法解析 {} 初始化
    3. 不可见的代理类可能会使auto从表达式中推导出“错误的”类型
  3. decltype 类型推导:
    1. 将decltype应用于变量名会产生该变量名的声明类型
    2. 返回decltype-》auto + decltype尾返回类型
    3. auto 与 decltype(auto)不同,decltype保留了auto忽略的左值引用
decltype(x+y) add(T x, U y){}; // 编译错误template <typename T, typename U>
auto add(T x, U y) -> decltype(x+y) 
{ return x+y; 
} template <typename T, typename U> // C++ 14
auto add(T x, U y) 
{ return x+y; 
}decltype(auto) f1()
{int x = 0;return x;                            //decltype(x)是int,所以f1返回int
}decltype(auto) f2()
{int x = 0;return (x);                          //decltype((x))是int&,所以f2返回int&
}
  1. 查看类型推导结果:
    1. 编译期:编译期报错诊断
    2. 运行时:
      1. typeid:依赖编译期解析,可能不正确
      2. Boost.TypeIndex
template<typename T>                //只对TD进行声明
class TD;                           //TD == "Type Displayer"
// 如果尝试实例化这个类模板就会引出一个错误消息,因为这里没有用来实例化的类模板定义。为了查看x和y的类型,只需要使用它们的类型去实例化TD:
TD<decltype(x)> xType;              //引出包含x和y
TD<decltype(y)> yType;              //的类型的错误消息template<typename T>
void f(const T& param)
{using std::cout;cout << "T =     " << typeid(T).name() << '\n';             //显示Tcout << "param = " << typeid(param).name() << '\n';         //显示
}                                                               //的类型#include <boost/type_index.hpp>template<typename T>
void f(const T& param)
{using std::cout;using boost::typeindex::type_id_with_cvr;//显示Tcout << "T =     "<< type_id_with_cvr<T>().pretty_name()<< '\n';    //显示param类型cout << "param = "<< type_id_with_cvr<decltype(param)>().pretty_name()<< '\n';
}

Modern C++:98 vs 11

  1. 构造,拷贝构造,拷贝复制
Widget w1;              //调用默认构造函数
Widget w2 = w1;         //不是赋值运算,调用拷贝构造函数
w1 = w2;                //是赋值运算,调用拷贝赋值运算符(copy operator=)
  1. 初始化:(),=,{}
    1. ():构造,无参构造和函数声明语义冲突,无参构造应该省略()
    2. =:拷贝构造,拷贝赋值
    3. {}:统一初始化,不允许类型变窄
  2. std::initializer_list 和 {} 初始化永远是最佳匹配
  3. () 初始化和 {} 初始化可能会有巨大不同:ex:std::vector
  4. 重载指针参数和整型参数:
    1. 0:int
    2. NULL:int or long
    3. nullptr:std::nullptr_t,可以隐式转换为指向任何内置类型的指针
  5. **typedef(C++98)和using(C++11):**using别名模版更简单,不需要::type和typename
template<typename T>                            //MyAllocList<T>是
using MyAllocList = std::list<T, MyAlloc<T>>;   //std::list<T, MyAlloc<T>>
MyAllocList<Widget> lw;                         //用户代码template<typename T>                            //MyAllocList<T>是
struct MyAllocList {                            //std::list<T, MyAlloc<T>>typedef std::list<T, MyAlloc<T>> type;      //的同义词  
};
MyAllocList<Widget>::type lw;                   //用户代码template<typename T>
class Widget {                              //Widget<T>含有一个
private:                                    //MyAllocLIst<T>对象typename MyAllocList<T>::type list;     //作为数据成员
}; template <class T> 
using remove_const_t = typename remove_const<T>::type;
template <class T> 
using remove_reference_t = typename remove_reference<T>::type;
template <class T> 
using add_lvalue_reference_t = typename add_lvalue_reference<T>::type; 
  1. 非限域Enum(C+98)和限域Enum(C++11):限域class enum不污染命名空间,都支持底层类型,但是Enum class不能隐式转换只能cast
enum Color { black, white, red };   //black, white, red在Color所在的作用域
auto white = false;                 //错误! white早已在这个作用域中声明enum class Color { black, white, red }; //black, white, red限制在Color域内
auto white = false;                     //没问题,域内没有其他“white”
Color c = white;                        //错误,域中没有枚举名叫white
Color c = Color::white;                 //没问题using UserInfo =                //类型别名,参见Item9std::tuple<std::string,     //名字std::string,     //email地址std::size_t> ;   //声望enum UserInfoFields { uiName, uiEmail, uiReputation };
UserInfo uInfo;                         //同之前一样
auto val = std::get<uiEmail>(uInfo);    //啊,获取用户email字段的值template<typename E>                //C++14
constexpr std::underlying_type_t<E>toUType(E enumerator) noexcept
{return static_cast<std::underlying_type_t<E>>(enumerator);
}
auto val = std::get<toUType(UserInfoFields::uiEmail)>(uInfo);
  1. 未定义私有声明(C++98)和deleted(C++11):
    1. 删除自动生成的函数:都可
    2. 删除隐式转换类型的函数重载:only deleted
template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
private:basic_ios(const basic_ios& );           // not definedbasic_ios& operator=(const basic_ios&); // not defined
};template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
public:basic_ios(const basic_ios& ) = delete;basic_ios& operator=(const basic_ios&) = delete;
};bool isLucky(int number);       //原始版本
bool isLucky(char) = delete;    //拒绝char
bool isLucky(bool) = delete;    //拒绝bool
bool isLucky(double) = delete;  //拒绝float和doubletemplate<>
void processPointer<void>(void*) = delete;
template<>
void processPointer<char>(char*) = delete;
class Widget {
public:template<typename T>void processPointer(T* ptr)
};
template<>                                          //还是public,
void Widget::processPointer<void>(void*) = delete;  //但是已经被删除了
  1. 显式override和final来实现重载和避免重载:C++11新增引用限定符
class Widget {
public:using DataType = std::vector<double>;DataType& data() &              //对于左值Widgets,{ return values; }              //返回左值DataType data() &&              //对于右值Widgets,{ return std::move(values); }   //返回右值
private:DataType values;
};
  1. 优先使用const_iterator而非iterator
  2. **begin,end,cbegin,rbegin的非成员函数通用性更强,**可用于原生数组
  3. **尽可能使用noexcept:**可以编译优化,noexcept函数可以调用异常中立函数
RetType function(params) noexcept;  //极尽所能优化	//C++11风格,没有来自f的异常
RetType function(params) throw();   //较少优化		//C++98风格,没有来自f的异常
RetType function(params);           //较少优化
  1. **const 常量,编译期不一定可知;constexpr 编译期常量,编译期可知;constexpr 函数值根据参数在编译期是否可知决定,**constexpr函数既可以是运行时也可以是编译期
  2. const成员函数中的mutable成员变量,需要mutex或atomic来保证线程安全
  3. 特殊成员函数:构造,析构,拷贝构造,拷贝赋值,移动构造,移动赋值
  4. **Role of Three:**自定义资源管理时,析构,拷贝构造,拷贝赋值同时自定义
  5. 移动操作仅当类没有显式声明移动操作,拷贝操作,析构函数时才自动生成
  6. 在自定义移动时,相应拷贝被deleted,在自定义析构时,拷贝不自动生成
  7. **置入emplace_back优于插入push_back:**避免了临时对象的创建和销毁

Smart Pointers

  • **unique_ptr:专有所有权,大小与原始指针相同,不占用额外内存资源,**完全拥有指向内容的所有权,只能移动不能复制,可以转化为shared_ptr
  • **shared_ptr:共享所有权,大小是原始指针二倍,包含引用计数,**引用计数在堆上,内存动态分配,原子操作增减
    • 使用maked_shared创建,或直接传递new指针,不要传递原始指针变量
    • 如果原始指针变量存在,且存在多个基于原始指针的shared_ptr,会存在多次析构的问题
  • **weak_ptr:从shared_ptr创建,不拥有所有权,**只能判断原指针是否过期,不能解引用
    • 获取shared_ptr:lock(),shared_ptr()
    • 避免shared_ptr循环引用无法释放
    • 作为缓存,观察者,只判断是否过期
  • make_shared, make_unique:
    • 异常安全,异常时不会导致内存泄漏
    • 对象内存和引用计数内存一次分配
    • 控制块和对象内存块绑定,存在weak_ptr时无法实际释放内存

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • Pimpl,Pointer to implementation:通过减少在类实现和类使用者之间的编译依赖来减少编译时间。(减少头文件中包含其他头文件的数量)
    • unique_ptr: 独占所有权,符合抽象,析构函数和移动拷贝函数的定义需要在不完整类型实现之后
    • shared_ptr:共享所有权,不符合抽象,运行时数据结构更大
// 原 header
class Widget() {                    //定义在头文件“widget.h”
public:Widget();private:std::string name;std::vector<double> data;Gadget g1, g2, g3;              //Gadget是用户自定义的类型
};// 新 header
class Widget {                          //仍然在“widget.h”中
public:Widget();~Widget();Widget(Widget&& rhs);               //只有声明Widget& operator=(Widget&& rhs);private:                                //跟之前一样struct Impl;std::unique_ptr<Impl> pImpl;
};// 新 cpp
#include <string>                   //跟之前一样,仍然在“widget.cpp”中struct Widget::Impl {};          //跟之前一样Widget::Widget()                    //跟之前一样
: pImpl(std::make_unique<Impl>())
{}Widget::~Widget() = default;        //跟之前一样Widget::Widget(Widget&& rhs) = default;             //这里定义
Widget& Widget::operator=(Widget&& rhs) = default;

Rvalue, Move & Forward

  • **move:**无条件将实参转化为右值,不一定移动构造,const变量无法移动时只能拷贝构造
  • **forward:**当且仅当实参为右值时,将其转化为右值
  • move 和 forward 都是** cast 操作,运行期无代码**
  • **通用引用:**T&&, auto&&,类型推导决定是左值引用还是右值引用
  • 函数返回值,传参,最后一次使用:右值引用使用move,通用引用使用forward
  • **返回值优化:**返回函数内的局部对象
    • 使用move移动操作来返回
    • 直接在返回值的内存中构建局部对象,消除拷贝操作
  • 通用引用参数的函数重载,优先级高于大部分函数
  • 完美转发的构造函数,non-const的优先级可能会大于拷贝构造和系统构造函数
  • 重载通用引用:
    • tag dispatch
template<typename T>
void logAndAdd(T&& name)
{logAndAddImpl(std::forward<T>(name),std::is_integral<typename std::remove_reference<T>::type>());
}template<typename T>                            //非整型实参:添加到全局数据结构中
void logAndAddImpl(T&& name, std::false_type)	//译者注:高亮std::false_type
{auto now = std::chrono::system_clock::now();log(now, "logAndAdd");names.emplace(std::forward<T>(name));
}std::string nameFromIdx(int idx);           //与条款26一样,整型实参:查找名字并用它调用logAndAdd
void logAndAddImpl(int idx, std::true_type) //译者注:高亮std::true_type
{logAndAdd(nameFromIdx(idx)); 
}
  • enable_if 约束
class Person  {                                         //C++14
public:template<typename T,typename = std::enable_if_t<                    //这儿更少的代码!std::is_base_of<Person,std::decay_t<T> //还有这儿>::value>                                    //还有这儿>explicit Person(T&& n);};
  • 引用折叠reference collapsing)。禁止声明引用的引用,但是编译器会在特定的上下文中产生这些。当编译器生成引用的引用时,引用折叠指导下一步发生什么。
    • 如果任一引用为左值引用,则结果为左值引用。否则(即,如果引用都是右值引用),结果为右值引用。
    • 折叠发生在四种情况下:模板实例化,auto类型推导,typedef与别名声明的创建和使用,decltype。
    • 通用引用就是在特定上下文的右值引用,上下文是通过类型推导区分左值还是右值,并且发生引用折叠的那些地方。
  • **移动语义的缺陷:**某些情况不存在,速度不如复制,非noexcept移动不可用
  • 完美转发perfect forwarding)意味着我们不仅转发对象,我们还转发显著的特征:它们的类型,是左值还是右值,是const还是volatile。
  • **完美转发失败:**花括号初始化,作为空指针的0或者NULL,仅有声明的整型static const数据成员,模板和重载函数的名字,位域。
template<typename T>
void fwd(T&& param)             //接受任何实参
{f(std::forward<T>(param));  //转发给f
}template<typename... Ts>
void fwd(Ts&&... params)            //接受任何实参
{f(std::forward<Ts>(params)...); //转发给f
}
  • 重载拷贝左值,移动右值-》完美转发-》直接传值移动:
    • 多一次额外的移动,适用移动开销低
      • 左值:拷贝+移动
      • 右值:移动+移动
    • 实现简单,不用重载,不用模版
class Widget {                                  //方法1:对左值和右值重载
public:void addName(const std::string& newName){ names.push_back(newName); } // rvaluesvoid addName(std::string&& newName){ names.push_back(std::move(newName)); }
private:std::vector<std::string> names;
};class Widget {                                  //方法2:使用通用引用
public:template<typename T>void addName(T&& newName){ names.push_back(std::forward<T>(newName)); }
};class Widget {                                  //方法3:传值
public:void addName(std::string newName){ names.push_back(std::move(newName)); }
};

Lambda

lambdas 和闭包类存在于编译期,闭包存在于运行时。

  • **按引用捕获:**捕获的变量超出生命周期,导致悬空引用
  • **按值捕获【默认】:**捕获指针,导致悬空引用;无法捕捉成员变量,只能捕捉this指针;无法捕捉static变量
  • **初始化捕获,可以捕获移动对象【移动捕获】:**c++11可以使用bind来模拟捕获移动对象
auto func = [pw = std::move(pw)]        //使用std::move(pw)初始化闭包数据成员{ return pw->isValidated()&& pw->isArchived(); };auto func = [pw = std::make_unique<Widget>()]   //使用调用make_unique得到的结果{ return pw->isValidated()          //初始化闭包数据成员&& pw->isArchived(); };
  • **泛型捕获:**捕获auto&&,C++11可以使用bind的完美转发来模拟泛型捕获
auto f = [](auto&& param){return func(std::forward<decltype(param)>(param));};auto f = [](auto&&... param){return func(std::forward<decltype(param)>(param)...);};
  • 使用lambda取代bind:
// lambda
auto setSoundL = [](Sound s) {using namespace std::chrono;using namespace std::literals;      //对于C++14后缀setAlarm(steady_clock::now() + 1h,	//C++14写法,但是含义同上s,30s);};// bind
using SetAlarm3ParamType = void(*)(Time t, Sound s, Duration d);auto setSoundB = std::bind(static_cast<SetAlarm3ParamType>(setAlarm), // 无法处理类型重载,需要显式制定类型std::bind(std::plus<>(), steady_clock::now(), 1h), // 需要推迟表达式求值,避免在调用实际函数前求值_1,30s);

Concurrency API

Concurrency support library (since C++11) - cppreference.com

concurrency library

  • :Mutual exclusion
    • lock_guard,unique_lock
  • <condition_variable>

atomic & mutex

  • **atomic:**原子操作,一个CPU指令实现
  • **mutex:**互斥锁,可由atomic实现加锁解锁操作,未拿到锁需要陷入内核完成线程挂起
  • **自旋锁:**可由atomic实现,未拿到锁不会挂起线程,效率高于互斥锁

atomic & volatile

  • std::atomic用在并发编程中,对访问特殊内存没用。
    • 可以限制编译器或底层硬件对代码进行重排和乱序执行,在不使用互斥锁时,避免并发编程时的数据竞争问题
  • volatile用于访问特殊内存,对并发编程没用。
    • 内存映射IO时,编译器不会优化重复读写,冗余访问,无用存储等代码

thread, pthread & cpu cores

  • 硬件线程:
    • 物理CPU个数:个人电脑1个插槽,服务器2个插槽
    • 物理CPU核心数【物理CPU的芯片组数量】:双核,四核,八核
    • 逻辑核=虚拟核=硬件线程【超线程,同步多线程技术】:8核16线程
  • 软件线程:
    • 系统线程,操作系统线程,操作系统调度到硬件线程上执行的线程
    • pthread(POSIX thread),windows thread
  • std::thread:
    • C++对象,软件线程的句柄
    • joinable:正在运行
    • unjoinable:
      • 默认构造
      • 已被移动
      • join:运行完毕
      • detach:与软件线程之间的连接关系被打断

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

thread & future

  • 基于线程异步执行:std::thread t(doAsyncWork);
    • 不能直接访问异步执行的结果,如果执行函数有异常抛出,代码会终止执行。
    • 自己处理线程耗尽、资源超额、负载均衡、平台适配等问题
    • 可以访问pthread api,使用thread pool
  • 基于任务异步执行:auto fut = std::async(doAsyncWork);
    • 可以获取异步执行的结果和异常
    • 标准库处理线程耗尽、资源超额、负载均衡、平台适配等问题
    • 默认std::launch::async | std::launch::deferred, 不保证执行,异步执行, std::launch::async强制异步执行

thread disconstruction

析构joinable的thread会导致程序终止。

使用RAII(“Resource Acquisition Is Initialization,资源获得即初始化)包装thread,在析构时进行join。

class ThreadRAII {
public:enum class DtorAction { join, detach };ThreadRAII(std::thread&& t, DtorAction a): action(a), t(std::move(t)) {}~ThreadRAII(){if (t.joinable()) {if (action == DtorAction::join) {t.join();} else {t.detach();}}}std::thread& get() { return t; }private:DtorAction action;std::thread t;
};

future disconstruction

image.png
析构future会导致隐式的join或隐式的detach:

  • 引用了强制异步任务的共享状态的最后一个future,隐式join,在异步执行完成之前阻塞
  • 其他future,隐式detach

thread communication: condition variable,atomic & future

  1. **condition variable + mutex:**需要结合互斥锁,且需要重复验证避免虚假唤醒
std::condition_variable cv;         //事件的条件变量
std::mutex m;                       //配合cv使用的mutex//检测事件
cv.notify_one();                    //通知反应任务//反应的准备工作
{                                       //开启关键部分std::unique_lock<std::mutex> lk(m); //锁住互斥锁cv.wait(lk);                        //释放锁后等待通知,通知后再次加锁//对事件进行反应(m已经上锁)
}                                       //关闭关键部分;通过lk的析构函数解锁m//继续反应动作(m现在未上锁)
  1. **atomic:**基于轮询,而不是阻塞
std::atomic<bool> flag(false);          //共享的flag//检测某个事件
flag = true;                            //告诉反应线程//准备作出反应
while (!flag);                          //等待事件//对事件作出反应
  1. **future:**使用了堆内存存储共享状态,只能使用一次通信
std::promise<void> p;                   //通信信道的promise//检测某个事件
p.set_value();                          //通知反应任务//准备作出反应
p.get_future().wait();                  //等待对应于p的那个future//对事件作出反应

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

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

相关文章

java学习part30callabel和线程池方式

140-多线程-线程的创建方式3、4&#xff1a;实现Callable与线程池_哔哩哔哩_bilibili 1.Callable 实现类 使用方式 返回值 2.线程池

检测判断IP合法性API接口

检测判断IP合法性API接口 一、检测判断IP合法性API接口二、使用步骤1、接口2、请求参数3、请求参数示例4、接口 返回示例 三、 如何获取appKey和uid1、申请appKey:2、获取appKey和uid 四、重要说明 一、检测判断IP合法性API接口 一款免费的帮助你检测判断IP合法性API接口 二、…

“于阗佛国、美食和田”——“万人游新疆”推广活动走进企业

11月23日&#xff0c;在安徽省文旅厅、安徽省援疆指挥部、和田地区文旅局的指导和支持下&#xff0c;由安徽环球文旅集团组织的“于阗佛国、美食和田”——“万人游新疆”分享会在安徽合肥市财富广场瑞众保险&#xff08;原华夏保险&#xff09;3楼黄山厅会议室举行&#xff0c…

【06】ES6:数组的扩展

一、扩展运算符 扩展运算符&#xff08;spread&#xff09;是三个点&#xff08;…&#xff09;。它是 rest 参数的逆运算&#xff0c;将一个数组转为用逗号分隔的参数序列。 1、基本语法 [...array] // array表示要转换的数组console.log(...[1, 2, 3]) // 1 2 3 console.l…

Python操作合并单元格

如何使用python操作Excel实现对合并单元格的一系列操作 01、准备工作&#xff08;使用镜像下载&#xff09; pip install openpyx -i https://pypi.tuna.tsinghua.edu.cn/simple 02、简单示例 简单创建一个工作簿进行示范&#xff1a; from openpyxl import Workbook from o…

波奇学C++:智能指针(二):auto_ptr, unique_ptr, shared_ptr,weak_ptr

C98到C11&#xff1a;智能指针分为auto_ptr, unique_ptr, shared_ptr&#xff0c;weak_ptr,这几种智能都是为了解决指针拷贝构造和赋值的问题 auto_ptr&#xff1a;允许拷贝&#xff0c;但只保留一个指向空间的指针。 管理权转移&#xff0c;把拷贝对象的资源管理权转移给拷贝…

vue中实现纯数字键盘

一、完整 代码展示 <template><div class"login"><div class"login-content"><img class"img" src"../../assets/image/loginPhone.png" /><el-card class"box-card"><div slot"hea…

阵列信号处理---频率-波数响应和波束方向图

波束延迟求和器 阵列是由一组全向阵元组成&#xff0c;阵元的位置为 p n p_n pn​&#xff0c;如下图所示&#xff1a; 阵元分别在对应的位置对信号进行空域采样&#xff0c;这样就产生了一组信号信号为 f ( t , p ) f(t,p) f(t,p),具体表示如下&#xff1a; f ( t , p ) [ f…

C++入门篇(零) C++入门篇概述

目录 一、C概述 1. 什么是C 2. C的发展史 3. C的工作领域 4. C关键字(C98) 二、C入门篇导论 一、C概述 1. 什么是C C是基于C语言而产生的计算机程序设计语言&#xff0c;支持多重编程模式&#xff0c;包括过程化程序设计、数据抽象、面向对象程序设计、泛型程序设计和设计模式…

SQL Server 2016(创建数据库)

1、实验环境。 某公司有一台已经安装了SQL Server 2016的服务器&#xff0c;现在需要新建数据库。 2、需求描述。 创建一个名为"db_class"的数据库&#xff0c;数据文件和日志文件初始大小设置为10MB&#xff0c;启用自动增长&#xff0c;数据库文件存放路径为C:\db…

Ubuntu系统CLion安装与Ubuntu下菜单启动图标设置

Ubuntu系统CLion安装 pycharm 同理。 参考官网安装过程&#xff1a;官网安装过程 下载linux tar.gz包 # 解压 sudo tar -xzvf CLion-*.tar.gz -C /opt/ sh /opt/clion-*/bin/clion.sh其中第二个命令是启动CLion命令 clion安装完以后&#xff0c;不会在桌面或者菜单栏建立图…

大学里学编程,为什么这么难?

在大学学习计算机专业&#xff0c;为何很多同学觉得编程学得不顺心呢&#xff1f;许多同学会有这种感觉&#xff0c;在上大学里的计算机专业课程时&#xff0c;听得头都大了&#xff0c;但是真正要写代码&#xff0c;却不知道从哪里开始&#xff0c;或是觉得&#xff0c;大学里…

05:2440----代码重定义

目录 一&#xff1a;引入 1&#xff1a;基本概念 2&#xff1a;NAND启动 3&#xff1a;NOR启动 4:变量 5&#xff1a;实验证明 A:代码makefile B:NOR启动 C:NAND启动 D:内存空间 二&#xff1a;链接脚本 1:NOR 2:NAND 3:解决方法 A:尝试解决 B:方法一解决 A:简…

【SparkSQL】SparkSQL的运行流程 Spark On Hive 分布式SQL执行引擎

【大家好&#xff0c;我是爱干饭的猿&#xff0c;本文重点介绍、SparkSQL的运行流程、 SparkSQL的自动优化、Catalyst优化器、SparkSQL的执行流程、Spark On Hive原理配置、分布式SQL执行引擎概念、代码JDBC连接。 后续会继续分享其他重要知识点总结&#xff0c;如果喜欢这篇文…

Echarts大屏可视化_05 折线图的定制开发

继续跟着pink老师学习Echarts相关内容&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 折线图1 1.引入 折线图选取示例地址 标题没有用到就给他删了 直接引入 注意这里是line下面的chart 获取dom元素一定不…

吉他初学者学习网站搭建系列(4)——如何查询和弦图

文章目录 背景实现ChordDbvexchords 背景 作为吉他初学者&#xff0c;如何根据和弦名快速查到和弦图是一个必不可少的功能。以往也许你会去翻和弦的书籍查询&#xff0c;像查新华字典那样&#xff0c;但是有了互联网后我们不必那样&#xff0c;只需要在网页上输入和弦名&#…

POSTGRESQL中如何利用SQL语句快速的进行同环比?

1. 引言 在数据驱动的时代&#xff0c;了解销售、收入或任何业务指标的同比和环比情况对企业决策至关重要。本文将深入介绍如何利用 PostgreSQL 和 SQL 语句快速、准确地进行这两种重要分析。 2. 数据准备 为了演示&#xff0c;假设我们有一张 sales 表&#xff0c;存储了销…

【PyTorch】线性回归

文章目录 1. 代码实现1.1 一元线性回归模型的训练 2. 代码解读2.1. tensorboardX2.1.1. tensorboardX的安装2.1.2. tensorboardX的使用 1. 代码实现 波士顿房价数据集下载 1.1 一元线性回归模型的训练 import numpy as np import torch import torch.nn as nn from torch.ut…

深度学习:什么是知识蒸馏(Knowledge Distillation)

1 概况 1.1 定义 知识蒸馏&#xff08;Knowledge Distillation&#xff09;是一种深度学习技术&#xff0c;旨在将一个复杂模型&#xff08;通常称为“教师模型”&#xff09;的知识转移到一个更简单、更小的模型&#xff08;称为“学生模型”&#xff09;中。这一技术由Hint…

二叉树遍历及应用

文章目录 前言构建二叉树前序遍历中序遍历后序遍历二叉树的结点个数二叉树的叶节点个数二叉树的高度二叉树第K层结点个数 前言 二叉树的遍历及应用主要是运用了递归、分治的思想。在这一篇文章&#xff0c;小编将介绍二叉树的前序遍历、中序遍历、后序遍历&#xff0c;求二叉树…