Linux C++快速入门

本文旨在以最小的篇幅,最少的信息,介绍最高频使用的内容,从而掌握C++编程开发的能力。

这种能力,只是语法层面,不涉及具体的函数库,基础库等内容。

能力准备:需要C语言基础。基本的if else, while,基础数据类型等等,不在本文涉及的范围之内。

环境准备

感谢微软的努力,让我们在 Windows环境下可以毫无障碍的进行Linux开发。

推荐使用Windows + wsl2 的环境开发。

打开Microsoft Store,搜索ubuntu,安装最新版本即可。

安装如有问题,请自行百度

使用 vscode + wsl插件的形式,编辑、编译代码。ctrl + ` 可以在命令行和文件编辑之间切换,非常的方便。

在ubuntu下,安装cmake, gcc, g++。安装方法自行百度。

代码的组织结构

build : 编译目录
docs : 文档目录,负责存放该代码相关的信息
libs : 该项目依赖的外部的库及头文件
libs/include 依赖库的头文件
libs/[编译器名称] 平台相关的库文件。比如cc放x86-64位的库,arm-linux-gnu-gcc放该编译器编译出的相关的库文件
source 项目源码目录
test 测试目录,内含测试代码

本文中所有涉及到的示例代码,可在此下载:

链接:https://pan.baidu.com/s/1f73k5uxYTRgtMEORbvgqvA?pwd=tnje

提取码:tnje

编译方法:

cd build
cmake ..
make

该代码展示了一个功能库的目录结构,编译方法。

如果想要做成可执行程序,参考test目录中的内容即可。

类的基本规则

通常定义一个类,我们会分为源文件.cpp,和头文件.h分开来用。

如下为头文件。其中的注释请仔细阅读

// rtspc.h
/*** @author * @brief rtsp客户端* @version 0.1* @date 2023-11-30* * @copyright Copyright (c) 2023* */
// 使用pragma once 让头文件只引用一次。与下面的 _RTSPC_H_ 作用一致 
#pragma once// 头文件避免重复引用。与#pragma once 二选一
#ifndef _RTSPC_H_
#define _RTSPC_H_#include <string>
#include <functional>// 注意,原则上禁止在头文件中使用using namespace xxx。避免命名空间失效
// 实际上,不管源文件和头文件,都不建议using namespace的方式。而是直接写全。
// using namespace std;// 这是命名空间。可以有效隔离类,函数,变量的重名问题。在定义库时都建议添加使用。
namespace rtsp
{class Rtspc
{
// 公共方法,类外部可访问
public:// 回调函数新写法。对应lambda表达式使用using OnData = std::function<void(const char *data, int len)>;Rtspc(bool btcp, OnData onData);// 注意,如果该类会被继承,则务必将它写成虚函数。否则影响析构virtual ~Rtspc();// 建议安装doxygen插件,在函数上方输入 /// 或者 /** 自动生成注释模板。/// @brief 公共方法,大写开头。私有方法,小写开头(代码规范,自行约定)。/// @param url /// @param bTCP /// @return int Run(const std::string &url, bool bTCP);/// @brief 停止/// @return void Stop(){_running = false;}// 保护方法。类内及类的子类可访问。
protected:// 对于不改变类的内容的方法,后面加const// 对于不希望被改变的返回的引用,前面加constconst std::string &getValue() const {return _url;}// 私有方法。通常它和私有成员的private分开写,更清晰一些。
private:void workthread();// 私有成员。成员变量通常为私有成员
private:std::string _url; // 成员变量以 '_' 开头,以便代码中与局部变量,参数做区分。bool _btcp;bool _running; OnData _onData;
};#endif //_RTSPC_H_} // namespace rtsp

源文件长这样。

// rtspc.cpp
#include <iostream>
#include <chrono>
#include <thread>
#include "rtspc.h"namespace rtsp
{// 构造函数
Rtspc::Rtspc(bool btcp, OnData onData)
// 这下面是类成员初始化的写法。据说比写在大括号里效率要高
:_btcp(btcp)
,_running(true)
,_onData(onData)
{}Rtspc::~Rtspc()
{
}int Rtspc::Run(const std::string &url, bool bTCP)
{std::cout << "Running " << url << std::endl;const std::string data = "haha, i am data";while (_running){std::this_thread::sleep_for(std::chrono::milliseconds(400));_onData(data.c_str(), data.size());}return 0;
}} // namespace rtsp

继承与虚函数

干货都在代码中

//rtp-pack.h 这是父类
#pragma once#include <memory>
#include <functional>namespace rtsp
{class RtpPack
{
public:// 回调打包好的数据using OnRtpData = std::function<void(const std::string &rtp)>;using Ptr = std::shared_ptr<RtpPack>;RtpPack(OnRtpData onRtp):_onRtp(onRtp){}// 注意,如果该类会被继承,则务必将它写成虚函数。否则影响析构virtual ~RtpPack(){}virtual int Pack(const uint8_t *data, int len) = 0;/// @brief 创建打包器/// @param encode 编码方式/// @return static Ptr CreatePacker(const char *encode, OnRtpData onData);
protected:OnRtpData _onRtp;
};} // namespace rtsp

// rtp-pack-h264.h 这是子类
#pragma once#include <rtp/rtp-pack.h>namespace rtsp
{
class RtpPackH264 : public RtpPack
{
private:/* data */
public:RtpPackH264(RtpPack::OnRtpData onRtp):RtpPack(onRtp){}virtual ~RtpPackH264(){}virtual int Pack(const uint8_t *data, int len) override{std::string rtp;rtp.append("begin flag");rtp.append((const char *) data, len);_onRtp(rtp);return 0;}// 非虚函数int Demo(){return 0;}
};} // namespace rtsp

如代码所示,RtpPackH264为子类,它继承于RtpPack

虚函数

其中,Pack 在RtpPack中,被定义为纯虚函数。这意味着你无法将RtpPack实例化。

也就是说,RtpPack pack; 是非法的。

只有在子类中实现了 Pack方法,就像RtpPackH264 一样,它才能够被实例化。

std::function 与lambda表达式

它是C++11开始支持的好东西,它有两个作用:

1,替代回调函数

2,替代回调函数的 param回传参数。

最重要的是第二点,结合lambda函数使用,让我们的代码看起来是如此的与众不同。

请参看如下示例。

testrtp.cpp

#include <rtp/rtp-pack-h264.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int main()
{int sock = socket(AF_INET, SOCK_STREAM, 0);connect(sock, xxx);// std::function 的定义与 lambda的应用rtsp::RtpPackH264 rtp([sock](const std::string &rtp){send(sock, rtp.c_str(), rtp.size());});while (1){rtp.Pack("123456", 6);}return 0;
}

可以看到,RtpPackH264 rtp 在实例化的时候,传的参数是一个奇怪的东西:[sock](const std::string &rtp){xxx

这个奇怪的东西,叫作lambda表达式。也叫匿名函数。

[]内部,就相当于我们注册回调函数时,注册进去的param,它通过回调函数再传回给我们。

而这里则不需要这么麻烦,你可以在[ ] 中加入任意多的变量,然后就如代码中的sock一样,在lambda体中使用。

需要注意的是,[sock]这是值传递的写法。它会记录sock的值。还可以这样写:[&sock],引用传递。此时需要注意,它相当于记录了sock的指针。

这里还有另一种写法,可以将lambda表达式写成一个变量:

    auto onRtp = [sock](const std::string &rtp){send(sock, rtp.c_str(), rtp.size());};rtsp::RtpPackH264 rtp(onRtp);

小提示:

本节中的代码,没有源文件。类的定义与实现,可以都写在头文件中,只不过这要看实际情况而写。

它的缺点是编译、链接较慢,封装性差。

但有些时候,比如模板,必须写在头文件中。

类的本质是什么?

C++中的类,命名空间,虚实函数,本质上都可以用C来表达。或者换个说法,C++编译器最终会把它变成C语言那样的东西。

就拿RtpPackH264来讲,它在编译器处理后,变成了如下的东西。至于C++的各种特性,都是语法糖。

rtp-pack-h264.c#include <stdlib.h>
#include <stdint.h>typedef void (*rtpCallback_t)(void *userparam, const uint8_t *data, int len);struct rtpH264_class{// 类成员变量rtpCallback_t _onRtp;void *_userparam;// 虚函数表struct rtpH264VirtualFunctionTable{int (*Pack)(struct rtpH264_class *thiz, const uint8_t *data, int len);}functionTable;
};// 虚函数的实现,对应rtsp::RtpPackH264::Pack
// 注意这奇怪的名字,param之后,列出了参数类型。这就是为什么C++允许重名但参数不同的函数。
int rtsp_RtpPackH264_Pack_param_u8_i32(struct rtpH264_class *thiz, const uint8_t *data, int len)
{return 0;
}// 构造函数,生成对象时自动调用。无论是new,还是局部变量
struct rtpH264_class *rtsp_RtpPackH264_RtpPackH264_param_rtpCallback_t_void(rtpCallback_t onRtp, void *userparam){// 分配内存struct rtpH264_class *rtp = malloc(sizeof(struct rtpH264_class));// 构造虚函数表rtp->functionTable.Pack = rtsp_RtpPackH264_Pack_param_u8_i32;rtp->_onRtp = onRtp;rtp->_userparam = userparam;return rtp;
}// 析构函数。在对象生命周期结束时,自动调用
void rtsp_RtpPackH264_del_RtpPackH264(struct rtpH264_class *rtp)
{free(rtp);
}// 非虚函数的实现
int rtsp_RtpPackH264_Demo(struct rtpH264_class *thiz)
{return 0;
}

以上代码中可以看到,类的函数的名字,实际上是由“命名空间+类名+方法名+参数类型”以一定规则,形成的。

而构造函数,实际上是编译器在生成对象是,帮我们调用的。

虚函数表,是在构造函数中指向了各个实际的函数。(不准确,但按此理解无不利影响)

敲黑板

所以,非虚函数,是在编译时就确定了调用关系的。比如调用RtpPackH264::Demo,是在编译时就确定了要调这个函数。

虚函数,是在执行时,查表,确定虚函数表中,指向的是哪个函数,从而完成调用。

请仔细研读,对照上述c实现的类代码,与类本身的关系。

请思考如下代码,最终输出的是什么?

class A{
public:void running(){printf("A running\n");}virtual void VirtualFunc(){printf("A virtual func\n");}
};
class B: public A{
public:void running(){printf("B running\n");}virtual void VirtualFunc(){printf("B virtual func\n");}
};void main()
{B b;A *a = &b;b.running();a->running();b.VirtualFunc();a->VirtualFunc();
}

搜索一下隐藏和覆盖,看看网上五花八门的解释,对照我们把C++的类改成C的写法,你能明白隐藏和覆盖是咋回事了吗?

还有lambda,std::function。

我们将lambda以基础的C++类的方式来实现,它是这样的:

rtp-pack-lambda.hpp (由 rtp-pack 的无lambda写法)

/*** @author LiuFengxiang (20451250@qq.com)* @brief 以C++的类模拟 lambda,捕获等行为* @version 0.1* @date 2023-12-01* * @copyright Copyright (c) 2023* */
#pragma once#include <string>namespace lambdaTest
{// 代替 std::function<void(const std::string &rtp)>
// std::function<xxx> 实际上是模板实例化成了一个类,这个类会记录函数指针和lambda捕获的变量
class RtpPackFunc
{
public:typedef void (*OnRtpData)(RtpPackFunc *thiz, const std::string &rtp);RtpPackFunc(OnRtpData data):_onRtp(data){}virtual ~RtpPackFunc(){}void Call(const std::string &rtp){_onRtp(this, rtp);}
public:OnRtpData _onRtp;
};class RtpPack
{
public:RtpPack(RtpPackFunc *callback):_callback(callback){}~RtpPack(){}void Pack(){_callback->Call("haha");}
private:RtpPackFunc *_callback;
};} // namespace lambdaTest

它的测试代码:testrtp-lambda.cpp, (由testrtp.cpp转化而来)

/*** @author LiuFengxiang (20451250@qq.com)* @brief 对应testrtp.cpp,我们不使用lambda,而是改用基础的类来实现* @version 0.1* @date 2023-12-01* * @copyright Copyright (c) 2023* */#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>#include <rtp/rtp-pack-lambda.hpp>using namespace lambdaTest;// 实际的实现并不相同,但是这样写起来优雅一点儿,也并不妨碍理解。
class MyRtpPackFunc :public RtpPackFunc
{
public:MyRtpPackFunc(RtpPackFunc::OnRtpData data, int sock):RtpPackFunc(data),_sock(sock){}virtual ~MyRtpPackFunc(){}
public:int _sock;
};// 编译器将lambda表达式生成了回调函数
static void MyOnRtpData(RtpPackFunc *func, const std::string &rtp)
{printf("%s running, data: %s\n", __func__, rtp.c_str());MyRtpPackFunc *mine = dynamic_cast<MyRtpPackFunc *> (func);if (mine != nullptr){// 不能真发,没准备好呢if (0)send(mine->_sock, rtp.c_str(), rtp.size(), 0);}
}int main()
{int sock = socket(AF_INET, SOCK_DGRAM, 0);// connect(sock, xxx);/*在使用lambda时,编译器干了很多事情:1,将匿名函数以自有的规则命名(objdump可以看一下,巨长),这里是 MyOnRtpData2,将 std::function模板实例化,相当于 MyRtpPackFunc3,将实例化的类生成对象,也就是这里的 func, 并传入初始化的两个参数: MyOnRtpData, sock*/ MyRtpPackFunc *func = new MyRtpPackFunc(MyOnRtpData, sock);// RtpPack记录的,实际上就是 std::function 的对象: funcRtpPack rtp(func);while (1){rtp.Pack();usleep(1*1000*1000);}}

为了简化写法,我们帮编译器翻译的并不精确,但这并不妨碍理解。

你就记住:lambda表达式,就是编译器帮你起名的匿名函数。而std::function 则是编译器帮你生成的类。

所谓捕获,同样没什么神奇之处,值捕获,在类中直接记录了该变量的值,引用捕获,则是在类中记录了该类的指针。

思考:值捕获和引用捕获的变量,它们的生命周期是怎样的?

本节是想告诉你,C++的很多规则,并不是人为制订出来的,而是语言本身的实现上,必须这么做。它的因果关系是:因为这门语言是这样设计的,所以,产生了这样的规则。

本节只是个引子,借此提示。

多思考!

多思考!!

多思考!!!

多思考背后的机理,那才能举一反三,抓住本质。

记住这句话:C++的所有规则,都是因为设计时,只能这样做。

最常用容器

std::string 其实也是容器。但是我们把它当成一个普通类用就好了

vector 是数组容器。用来管理数量不定的同类型的内容

map 相当于一个映射表,key,value的形式。通过key可以快速的查找到对应的值。

如下代码展示了一些常用的函数,更详细内容查文档:DevDocs

/*** @author LiuFengxiang (20451250@qq.com)* @brief 介绍常用容器的用法* @version 0.1* @date 2023-12-01* * @copyright Copyright (c) 2023* */#include <vector>
#include <string>
#include <map>
#include <iostream>#include <memory> // 智能指针相关class Node
{
public:// 智能指针的技巧。简化写法// 使用的时候 Node::Ptr p 相当于 std::shared_ptr<Node> pusing Ptr = std::shared_ptr<Node>;Node(int val, const std::string &str):_val(val),_str(str){}virtual ~Node(){}const std::string &Str()const {return _str;}int Val()const {return _val;}void SetVal(int val){_val = val;}private:int _val;std::string _str;
};static void vectorDemo()
{// 使用智能指针代替Node * 或者 直接Node。// 如果用指针,在释放时必须逐个 delete。一但遗漏就会有内存泄漏// 如果直接用Node,则每次加入时都会产生拷贝动作。std::vector<Node::Ptr> vec;for (int i = 0; i < 10; i++){auto node = std::make_shared<Node>(i, "haha");vec.push_back(node);}// 遍历,并按条件删除for (auto it=vec.begin();it!= vec.end();){if ((*it)->Val() == 5){// 删除成员时,不能直接it++it = vec.erase(it);}else{it++;}}// 另一种遍历方式for (auto &&it : vec){printf("node val: %d, str: %s\n", it->Val(), it->Str().c_str());it->SetVal(it->Val()+1);}// 注意,如果是 std::vector<Node *> vec, vec.clear() 执行时,并不能自动对每个node做delete动作。// 所以此时,你需要先逐个 delete ,再行 clearvec.clear();
}// map 主要是为了快速通过key 来找到对应的内容。key可以是基础类型,string
// 如果要把自定义的类作为key,则需要自定义比较函数
void mapDemo()
{std::map<int, Node::Ptr> _map;for (int i = 0; i < 10; i++){auto node = std::make_shared<Node>(i, "haha");_map.emplace(i, node);}// 查找int key = 5;auto it = _map.find(key);if (it != _map.end()){std::cout << "we Found it, key: " << it->first << ", str: " << it->second->Str() << std::endl;}else{std::cout << "We Failed found with key: " << key << std::endl;}// 遍历,并按条件删除for (auto it=_map.begin();it!= _map.end();){if (it->first == 5){// 删除成员时,不能直接it++it = _map.erase(it);}else{it++;}}// 另一种遍历方式for (auto &&it : _map){std::cout << "node key: " << it.first << ", val: " << it.second->Str() << std::endl;it.second->SetVal(it.second->Val()+1);}
}int main()
{vectorDemo();mapDemo();return 0;
}

智能指针的使用

智能指针,是现代C++编程非常重要的一个特性。

实际上,有了智能指针之后,我们不应该再使用裸指针了。

下面罗列几个主要的使用场景:

1,配合容器使用

比如有一个类Car,它有很多成员。

如果定义std::vector<Car> _carvec,它的问题是:

Car需要可拷贝,有可能需要实现拷贝构造函数

Car是拷贝了多份的,是独立的。它们之间互相完全无关。

而如果使用指针 std::vector<Car *> _pcarvec。

那你需要注意的是:插入前要new Car, 擦除前要先 delete 成员。

最容易忘的是_pcarvec.clear(). 这个方法执行前,你需要先遍历,逐个delete car

此时,更方便的用法是:std::vector<std::shared_ptr<Car>> _shCarVec;

2,回调函数中使用weak_ptr

(weak_ptr的概念可以先百度一下。)

回调函数有个比较大的问题是,当回调上来之后,数据的消费者可能已经被销毁了。这时我们的指针,是否还生效?如何判断?

如下代码中,rtspc的回调,数据上来之后,窗口是否还存在?

这里,我们通过保存它的weak_ptr句柄,使用时,通过lock的形式来处理。

只要lock成功了,weak_ptr将会升级成为强引用,说明对象还在,我们就可以正常输入数据。

//rtspclient.cpp
#include <memory>
#include <map>
#include <vector>
#include <mutex>
#include <thread>
#include <rtspc/rtspc.h>// 解码窗口
class MyWindow
{
public:using Ptr = std::shared_ptr<MyWindow>;MyWindow(int winid):_winid(winid){}~MyWindow(){printf("window %d destroyed\n", _winid);}int InputMediaData(const char *data, int len){// printf("input data in window: %d\n", _winid);return 0;}
private:int _winid;
};class WindowMgr
{
private:WindowMgr(/* args */){}~WindowMgr(){}
public:// 单例static WindowMgr& Instance(){static WindowMgr _inst;return _inst;}void SetWindowCnt(int cnt){std::lock_guard<std::mutex> guard(_mutex);// 注意哦,这里窗口重建了if ((size_t)cnt != _windows.size()){_windows.clear();// 延时,扩大rtspc上回调时窗口销毁的概率std::this_thread::sleep_for(std::chrono::milliseconds(1000));for (int i = 0; i < cnt; i++){_windows.push_back(std::make_shared<MyWindow>(i));}}}MyWindow::Ptr GetWindow(int winid){std::lock_guard<std::mutex> guard(_mutex);if ((size_t)winid >= _windows.size()){return nullptr;}return _windows[winid];}private:std::vector<std::shared_ptr<MyWindow> > _windows;std::mutex _mutex;
};static void PlayInWindow(int winid, const char *url){std::weak_ptr<MyWindow> weak = WindowMgr::Instance().GetWindow(winid);rtsp::Rtspc rtspc(true, [&rtspc, weak](const char *data, int len){std::shared_ptr<MyWindow> strongPtr = weak.lock();if (strongPtr == nullptr){printf("window destroyed, exit rtspc\n");rtspc.Stop();}else{strongPtr->InputMediaData(data, len);}});rtspc.Run(url, true);
}static void SetWindowCnt(int winCnt)
{printf("Now win cnt: %d\n", winCnt);WindowMgr::Instance().SetWindowCnt(winCnt);for (int i = 0; i < winCnt; i++){std::thread([i](){char url[256];snprintf(url, sizeof(url), "rtsp://192.168.1.2:554/live/chn%d", i);PlayInWindow(i, url);}).detach();}
}int main(int argc, const char *argv[])
{int count = 4;while (true){SetWindowCnt(count);getchar();count++;}return 0; 
}

可能还有同学有疑问,如果strongPtr拿到之后,在InputMediaData执行之前,发生了窗口切换怎么办呢?

这完全无须担心,由于我们已经持有了window的强引用,此时它并不会被销毁。只有等我们InputMediaData执行完之后,rtspc的回调函数执行完,strongPtr的生命周期完结,此时智能指针的计数清零,MyWindow才会得到释放。

3,类成员指针

类成员指针,它的重建,需要先delete老的。析构时,也需要析构。

而使用了智能指针,这些工作都不需要做了

如下代码中,智能指针_packer,构造函数中的创建,ChangePacker函数中把它重新赋值,都不需要考虑销毁。因为智能指针会自动析构老的内容。

同时,~Rtsps()析构函数执行时,也不需要手工析构_packer。

/*** @author LiuFengxiang (20451250@qq.com)* @brief rtsp 服务端* @version 0.1* @date 2023-12-9* * @copyright Copyright (c) 2023* */
#pragma once
#include <string>
#include <rtp/rtp-pack.h>namespace rtsp
{class Rtsps
{
public:Rtsps(/* args */){_packer = RtpPack::CreatePacker("H264", [this](const std::string &rtp){onRtpData(rtp);});}// 注意,如果该类会被继承,则务必将它写成虚函数。否则影响析构virtual ~Rtsps(){}/// @brief 更换打包器/// @param encode 打包器名称void ChangePacker(const char *encode){_packer = RtpPack::CreatePacker(encode, [this](const std::string &rtp){onRtpData(rtp);});}int Run(){return 0;}
private:void onRtpData(const std::string &rtp){printf("onRtpData\n");}private:// 打包器的句柄。RtpPack::Ptr _packer;
};} // namespace rtsp

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

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

相关文章

JVM之jmap java内存映射工具

jmap java内存映射工具 1、jmap jdk安装后会自带一些小工具&#xff0c;jmap命令(Memory Map for Java)是其中之一。主要用于打印指定Java进程(或核 心文件、远程调试服务器)的共享对象内存映射或堆内存细节。 jmap命令可以获得运行中的jvm的堆的快照&#xff0c;从而可以离…

[React]基于Antd的FormModal的组件封装以及useFormModal的hooks封装

[React]基于Antd的FormModal的组件封装以及useFormModal的hooks封装 场景 很常见&#xff0c;打开弹窗输入表单等… 封装后&#xff0c;弹窗自行挂载到body上&#xff0c;只需关注表达逻辑和打开关闭逻辑&#xff0c;其它的已经帮你管理好了 源码 import React, { useRef,…

使用 Timm 库替换 YOLOv8 主干网络 | 1000+ 主干融合YOLOv8

文章目录 前言版本差异说明替换方法parse_moedl( ) 方法_predict_once( ) 方法修改 yaml ,加载主干论文引用timm 是一个包含最先进计算机视觉模型、层、工具、优化器、调度器、数据加载器、数据增强和训练/评估脚本的库。 该库内置了 700 多个预训练模型,并且设计灵活易用。…

开发中需要防止用户哪些骚操作(测试鸭通关总结)

首先的话作为一个标准安全的后台认证和鉴权是一定要做好的&#xff0c;除此之外一下业务场景和常见问题也要进行考虑和预防。 一.收藏和点赞 网页前端和后端都要对点赞和收藏状态进行控制。简单说就是已经点赞和收藏的用户不能再进行点赞和收藏。 二.频繁请求某个接口 解决…

经典策略筛选-20231213

策略1&#xff1a; 龙头战法只做最强&#xff1a;国企改革 ----四川金顶 1、十日交易内出现 涨停或 &#xff08;涨幅大于7个点且量比大于3&#xff09; 2、JDK MACD RSI OBV LWR MTM 六指标共振 3、均线多头 4、 筹码峰 &#xff08;锁仓&#xff09; 5、现价> 五日均…

十二.镜头知识之镜头分辨率(解析力)

十二.镜头知识之镜头分辨率(解析力) 文章目录 十二.镜头知识之镜头分辨率(解析力)12.1 **分辨率与解像力的定义**12.1.0 分辨率定义12.1.1 解像力的定义12.1.2 解像力是分辨率的倒数12.1.3 Wavelength 对 物方分辨率的影响12.2 镜头分辨率跟相机分辨率如何才能匹配12.2.1 镜…

OkHttp: 拦截器和事件监听器

文章目录 1. 拦截器1. 拦截器链2. 实际案例1. 注册为应用拦截器2. 注册为网络拦截器 3. 如何选择用哪种拦截器1. 应用拦截器2. 网络层拦截器3. 重写请求4. 重写响应 4. 可用性 2. 事件监听器1. 请求的生命周期2. EventListener使用案例3. EventListener.Factory4. 调用失败的请…

【LeetCode】28. 找出字符串中第一个匹配项的下标 【字符串单模匹配:KMP算法】

题目链接 Python3 直觉解法 class Solution:def strStr(self, haystack: str, needle: str) -> int:pn, ph 0, 0n len(needle) h len(haystack)while ph < h:if haystack[ph] needle[pn]:if pn n-1: # 1234 123return ph - len(needle) 1else: pn 1ph 1else:…

RocketMQ的监控和管理工具有哪些❓

RocketMQ 提供了一些监控和管理工具&#xff0c;以便于用户对消息中间件的运行状态进行监控、管理和调优。以下是一些常用的 RocketMQ 监控和管理工具&#xff1a; 1. RocketMQ Console RocketMQ Console 是 RocketMQ 官方提供的监控和管理工具&#xff0c;提供了直观的图形界…

【LeetCode刷题】-- 163.缺失的区间

163.缺失的区间 class Solution {public List<List<Integer>> findMissingRanges(int[] nums, int lower, int upper) {List<List<Integer>> res new ArrayList<>();for(int num : nums){if(lower < num){res.add(Arrays.asList(lower,num -…

c语言函数与指针

//本文有待补充。 一、函数 1.函数的定义 如果程序的逻辑比较复杂、代码量比较大&#xff0c;或者重复性功能比较多&#xff0c;那么全部写在主函数里就会显得十分冗长和杂乱。为了使代码更加简洁、思路更加清晰&#xff0c;C语言提供了”函数“。函数是一个实现一定功能的语…

dockerfile创建镜像 lNMP+wordpress

dockerfile创建镜像 lNMPwordpress nginx dockernginx mysql dockermysql php dockerphp nginx vim nginx.conf vim Dockerfile docker network create --subnet172.17.0.0/16 --opt "com.docker.network.bridge.name""docker1" mynetwork docker buil…

Web是什么?它具体的功能是什么?它值不值得我们去学习?我们该如何去学习?

Web是指“World Wide Web”的缩写&#xff0c;是互联网上的一种信息系统&#xff0c;通过超文本链接方式将全球各地的文档链接在一起&#xff0c;形成一个巨大的信息资源库。Web的基本构成包括网页、超文本传输协议&#xff08;HTTP&#xff09;、网页浏览器等。 网页是Web的基…

失败的2x2 Mipi Raw10转RGB565

反转了&#xff0c;有思路改了&#xff0c;待我思考一番。 --------------------------------------------------------------------------------------------------------------------------------- 希望完成的目标&#xff1a; MIPI在解析以后是四个像素四个像素地产出数据…

最新Applestore建立其他地区账号简单快捷一看就会

1、首先打开创建appleid网站 2、点击创建你的Apple ID开始创建&#xff08;这里以美国为例&#xff09; 电话号码可以填大陆手机号即可 这两个选项建议不要勾选 3、更改付款方式 3.1点击付款与配送 3.2添加付款方式&#xff0c;这里是最重要的一步&#xff0c;传统方法已经无法…

真正的力量不是摧毁,而是在困境中保持微笑,坚持向前。

真正的力量不是摧毁&#xff0c;而是在困境中保持微笑&#xff0c;坚持向前。

Mac电脑投屏AirServer 2024怎么下载安装激活许可期限

对于那些想要将 iPhone、iPad 或其他 iOS 设备上的小屏幕镜像到计算机上的大屏幕的人来说&#xff0c;AirPlay 是一个很好的工具。 基于此&#xff0c;AirServer 非常需要将您的 Mac 或 PC 变成 AirPlay 设备。 但是如何使用计算机上的设置对 iPhone 等 iOS 设备进行屏幕镜像&a…

docker save多个镜像打包成一个tar.gz压缩文件

docker save多个镜像打包成一个tar.gz压缩文件 有时候我们需要将docker中的多个镜像批量的传输到另一台机器&#xff0c;如果通过docker save这种命令则需要制作多个tar文件&#xff0c;这样以来冗余的操作较多而且tar文件占据的空间较大&#xff0c;不利于传输。 可以通过以…

Netperf使用总结

什么是Netperf Netperf是由惠普公司开发的一种网络性能测量工具&#xff0c;主要针对基于TCP或UDP的传输。Netperf根据应用的不同&#xff0c;可以进行不同模式的网络性能测试&#xff0c;即批量数据传输&#xff08;bulk data transfer&#xff09;模式和请求/应答&#xff0…

Ubuntu 22安装PHP环境

参考博客为《练习 0&#xff08;2/2&#xff09;&#xff1a;Ubuntu 环境下安装PHP&#xff08;PHP-FPM&#xff09;》和《原生态Ubuntu部署LAMP环境 PHP8.1MySQLApache》 sudo apt-get install -y php7.4想要安装php7.4&#xff0c;发现安装的是php8.1。 完成如下图&#xf…