[C++ QT项目实战]----C++ QT系统实现多线程通信

前言

        在C++ QT中,多线程通信原理主要涉及到信号与槽机制和事件循环机制。

        1、信号与槽机制:
        在QT中,信号与槽是一种用于对象间通信的机制。对象可以通过发送信号来通知其他对象,其他对象通过连接槽来接收信号并进行相应的处理。这个机制可以跨线程使用,即一个线程可以发送信号,另一个线程可以接收信号。

        在使用多线程时,通过将一个对象的信号与另一个对象的槽连接起来,可以在不同线程中进行通信。当信号发出时,QT会在接收对象所在的线程中调用槽函数,从而实现多线程之间的通信。

        2、事件循环机制:
        在QT中,每个线程都有自己的事件循环。事件循环是处理事件的机制,包括用户输入、定时器事件等。当一个线程启动时,会进入事件循环,等待事件发生并处理事件。

        在多线程通信中,通常会涉及到在不同线程之间传递事件。通过在一个线程中发出事件,另一个线程中的事件循环可以接收并处理这个事件,从而实现线程间的通信。

        总的来说,C++ QT中的多线程通信原理是通过信号与槽机制和事件循环机制来实现的。通过使用这两种机制,可以在不同线程之间进行通信,并实现多线程之间的协作。但需要注意线程安全性,避免出现竞态条件和线程安全问题。

正文

01-功能演示

        如下图所示,第一张图,是经过运行之后,进入主界面,会同时显示后台信息,黑色的控制台界面就是线程运行信息,是否有两个线程也可以查看,主界面可以通过控件控制进程运行状态。        

        注:系统资源竞态是指多个线程或进程在对共享资源进行访问时,由于执行顺序不确定或未正确同步,导致竞争条件发生,从而导致数据不一致或程序出现错误的现象。

        当多个线程同时访问共享资源时,如果没有使用适当的同步机制来确保每个线程对资源的访问顺序,就可能引发系统资源竞态。系统资源竞态会导致一些严重问题,包括但不限于以下几点:

        数据不一致性:如果多个线程同时访问共享数据但没有正确同步,可能会导致数据不一致的情况。例如,一个线程正在修改数据的同时,另一个线程也在读取或修改相同的数据,可能会导致数据错误或不一致。

        资源泄漏:当多个线程同时竞争资源而没有正确释放资源,可能会导致资源泄漏。例如,并发写入文件时,如果某个线程在写入文件后未正确关闭文件句柄,可能会导致文件描述符泄漏。

        死锁:系统资源竞态还可能导致死锁的发生。当两个或多个线程相互等待对方释放资源时,可能导致所有参与的线程无法继续正常执行,从而导致程序挂起。

        因此,在采用系统多线程运行时,一定要使用一些方法防止资源竞争,可以采取以下几种措施:a、使用互斥量(Mutex)或信号量(Semaphore)等同步机制,在访问共享资源时确保只有一个线程可以访问;b、 使用临界区(Critical Section)来限制对共享资源的访问,确保线程之间的同步;c、使用条件变量(Condition Variable)在需要等待某个条件满足时进行线程等待和唤醒。

        这里的系统使用的是数据库进行资源交换和读取等,线程之间互不干扰,不存在线程竞争问题。

         下面是主线程和子线程之间通信的几个案例,也是系统中运行产生的,如下图所示,系统改为手动模式,首先点击打开配置文件按钮,可以看出控制台运行信息为“配置文件config.ini已打开!”,点击平台数据读取按钮,显示下方运行信息,这就是主线程和子线程之间的交互信息,可以通过QT中信号与槽函数的形式实现。

        下面重点分析如何实现,并附代码解释。

02-系统多线程实现

        这里讲解四个实现函数文件,其实是两个文件:mainwindow.h和mainwindow.cpp文件为主线程文件Zanj_WFC_ctrl.h和Zanj_WFC_ctrl.cpp文件为子线程文件,下面一一解释。

        mainwindow.cpp文件

        (1)、该文件用于实现显示主线程中的主界面,也就是运行之后,第一个界面,可以在界面上进行操作,后台信息是由于spdlog日志信息的导入,才会进行信息显示,便于观察,#include "spdlog/spdlog.h",代码中下面这一部分就是建立子线程的核心,QT中可以使用moveToThread()函数建立新线程。具体介绍如下:

        a、m_Zanj_WFC_ctrl = new Zanj_WFC_ctrl;// 这个实例要负责预报计算任务
        b、m_Zanj_WFC_ctrl_Thread = new QThread; // 子线程,本身不负责预报计算
        c、connectZanj_WFC_ctrl(m_Zanj_WFC_ctrl); // 连接信号-槽,复制的开始和取消指令是通过信号发送的
        d、m_Zanj_WFC_ctrl->moveToThread(m_Zanj_WFC_ctrl_Thread);// 将实例移动到新的线程,实现多线程运行
        e、m_Zanj_WFC_ctrl_Thread->start();

        (2)、函数connectZanj_WFC_ctrl()用于连接子线程函数,里面有该函数实现的功能,使用connect()函数编写代码: connect(this, SIGNAL(system_predict_init_link()), m_Zanj_WFC_ctrl, SLOT(HDGL_SYSTEM_Init()));这个函数HDGL_SYSTEM_Init()在子线程中实现,也就是说只要主线程启动,子线程也会跟着启动,可以在该函数中实现子线程的功能。

        (3)、上述描述的主线程与子线程通信的案例中的实现过程如下:也是通过信号与槽函数方式,比如打开配置文件函数,首先对该函数进行实现on_Open_config_Btn_clicked(),该函数就是打开配置文件控件对应的函数,该函数实现了点击按钮之后,发送了信号emit send_config();该信号通过connect()函数发送到子线程,代码为connect(this, &MainWindow::send_config, m_Zanj_WFC_ctrl, &Zanj_WFC_ctrl::OnconfigSlot);OnconfigSlot()函数在子线程中实现。

        原理就是这样,虽有些繁琐,但是逻辑性强。另一个读取数据按钮原理一样。

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "Zanj_WFC_ctrl.h"
#include "qdebug.h"
#include <qstring.h>

#include "cglobal.h"
#include <QElapsedTimer>
#include <iostream>
#include "windows.h"
#pragma comment(lib,".\\lib\\fmtd.lib")

#include "spdlog/fmt/ostr.h"
#include "spdlog/spdlog.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/rotating_file_sink.h"


#include  <QTimer>
#include  <time.h>
#include  <QLabel>
#include  <QValueAxis>
#include  <QMargins>
#include  <QtWidgets/QApplication>
#include  <QtWidgets/QMainWindow>

#include <ctime>  

using namespace std::literals;
using namespace std;
namespace spd = spdlog;
auto console_mainWindow = spd::stdout_color_mt("主界面控制台");
auto rotating_logger_mainWindow = spdlog::rotating_logger_mt("CAO8_Winmain_rotating", "logs/CAO8_rotating.logger", 1048576 * 5, 12);

MainWindow::MainWindow(QWidget *parent) :
	QMainWindow(parent),
	ui(new Ui::MainWindow)
{
	ui->setupUi(this);
	setWindowTitle(QString::fromLocal8Bit("主系统")); // 此处写应用程序在标题栏上显示的名称
	setStyleSheet("background - color:pink; ");
	reshow();
	data_log_tblview();
	console_mainWindow->info("欢迎进入主系统UI线程[Mainwindow]!");
	rotating_logger_mainWindow->info("欢迎进入主系统UI线程[Mainwindow]!");
	 

	m_Zanj_WFC_ctrl = new Zanj_WFC_ctrl;// 这个实例要负责预报计算任务
	m_Zanj_WFC_ctrl_Thread = new QThread; // 子线程,本身不负责预报计算
	connectZanj_WFC_ctrl(m_Zanj_WFC_ctrl); // 连接信号-槽,复制的开始和取消指令是通过信号发送的
	m_Zanj_WFC_ctrl->moveToThread(m_Zanj_WFC_ctrl_Thread);// 将实例移动到新的线程,实现多线程运行
	m_Zanj_WFC_ctrl_Thread->start();

	emit system_predict_init_link();

	
	// 向子线程发送打开配置文件信息
	connect(this, &MainWindow::send_config, m_Zanj_WFC_ctrl, &Zanj_WFC_ctrl::OnconfigSlot);
	// 向子线程发送读取数据信号,子线程接收后,进行数据读取
	connect(this, &MainWindow::send_rChk_ToWFC, m_Zanj_WFC_ctrl, &Zanj_WFC_ctrl::OnGETSlot);

}

MainWindow::~MainWindow()
{
	delete ui;
}

// 这里连接子线程,进行子线程的开启,子线程连接数据库,连接大数据平台,打开所需文件的信号传递,以及MainWindow界面槽函数处理信号的实现
void MainWindow::connectZanj_WFC_ctrl(Zanj_WFC_ctrl * m_Zanj_WFC_ctrl)
{
	connect(this, SIGNAL(system_predict_init_link()), m_Zanj_WFC_ctrl, SLOT(HDGL_SYSTEM_Init()));// 使用信号-槽机制,发出开始指令

}

// 打开配置文件函数
void MainWindow::on_Open_config_Btn_clicked()
{
	ui->Iplate4j_IP_QLE->setText("127.0.0.1");
	ui->model_PC_IP_QLE->setText("127.0.0.1");
	emit send_config();
}

// 由主窗口发送数据读取信号,子线程实现数据读取操作
void MainWindow::on_Restful_data_read_Btn_clicked()
{
	emit send_rChk_ToWFC();

}
        mainwindow.h文件

        该文件就是.cpp文件中众多函数的声明,以及信号的声明,比如读取数据时发送的信号需要在这里声明一下,便于使用。Zanj_WFC_ctrl * m_Zanj_WFC_ctrl; // 预报计算的类
    QThread * m_Zanj_WFC_ctrl_Thread; //将被移动到此线程执行
,这里也定义了指针,用于在.cpp文件中使用。

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include"ui_mainwindow.h"
#include <QMainWindow>
#include <QLabel>
#include <QtSql/QtSql>

#include "spdlog/fmt/ostr.h"
#include "Zanj_WFC_ctrl.h"
#include <QVariant>
#include "wfc_to_main.h"

#include <QtSql/QtSql>
#include <QDateTime>
#include <QTimer>
#include <qtextcodec.h> 
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>

#include <QMouseEvent>
//一定要声明!!!
#include"dialog_data_log.h"
//#include<vector>

//#include "dialog_data_log.h"
namespace Ui {
	class MainWindow;
}

class MainWindow : public QMainWindow
{
	Q_OBJECT

public:
	explicit MainWindow(QWidget *parent = nullptr);
	~MainWindow();

	Zanj_WFC_ctrl * m_Zanj_WFC_ctrl; // 预报计算的类
	QThread * m_Zanj_WFC_ctrl_Thread; //m_Mp_Pred将被移动到此线程执行
	void connectZanj_WFC_ctrl(Zanj_WFC_ctrl *m_Zanj_WFC_ctrl);

private slots :

	void on_Restful_data_read_Btn_clicked();   //点击平台数据读取按钮,实现读取数据信号发送	
	void on_Open_config_Btn_clicked();


signals:
	
	void system_predict_init_link();//定义向子线程发送的信号,用于实现子线程开启
    void send_config();      // 定义向子线程发送打开配置文件的信号
	void send_rChk_ToWFC();  // 定义向子线程发送数据读取的信号
	
private:
	Ui::MainWindow *ui;

	QLabel * pLabel;

};

#endif // MAINWINDOW_H
        Zanj_WFC_ctrl.cpp文件

        (1)、该文件中的众多函数就是对主线程发送的信号的实现过程,当接受到主线程的信号之后,如果需要对其进行处理,便可以在这里定义函数实现过程,该HDGL_SYSTEM_Init()函数就是对主线程中connectZanj_WFC_ctrl()连接函数的实现。

         (2)、案例中主线程发送读取数据信号,子线程中函数的实现OnGETSlot()在该文件中,这里适用于读取数据函数的实现,可以根据需要进行修改。打开配置文件的实现原理与之一致。

#include "Zanj_WFC_ctrl.h"
#include "wfc_to_main.h"
#include <Python.h>
#include "qdebug.h"
#include <qstring.h>
#include <Python.h>

#pragma comment(lib,"mclmcrrt.lib")  
#pragma comment(lib,"libmx.lib")  
#pragma comment(lib,"libmat.lib")  
#pragma comment(lib,"mclmcr.lib")  
#pragma comment(lib,".\\lib\\CGL_Mech_Property_BP_pred.lib")
#pragma comment(lib,".\\lib\\fmtd.lib")


#include <QAxObject>
#include <QStandardPaths>
#include <QEventLoop>
#include <QDesktopWidget>

#include <algorithm>
#include <numeric>
#include <vector>
#include <QVector>
#include <stdlib.h>

#include "spdlog/fmt/ostr.h"
#include "spdlog/spdlog.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/rotating_file_sink.h"

using namespace libxl;
using namespace std::literals;
//using namespace fmt;
using namespace std;
namespace spd = spdlog;
auto console_WFC_thread = spd::stdout_color_mt("WFC计算线程 ");
auto rlogger_WFC_thread = spdlog::rotating_logger_mt("CAO8_WFC_thread_rotating", "logs/CAO8_rotating.logger", 1048576 * 5, 12);


int Zanj_WFC_ctrl::HDGL_SYSTEM_Init()
{

	rlogger_WFC_thread->flush_on(spd::level::debug);

	console_WFC_thread->info("欢迎进入主系统计算线程");
	str_info = QString::fromLocal8Bit("0 应用程序环境路径  ") + path_deploy;

	return 1;
}

void Zanj_WFC_ctrl::OnGETSlot()
{
	QString strMessage;
	QString strResult;
	QString strUrl;
	QString strInput;
	QFile file(QApplication::applicationDirPath() + "/1.json");
	if (!file.open(QIODevice::WriteOnly)) {
		console_WFC_thread->error("Json file open failed! ");
		rlogger_WFC_thread->error("Json file open failed! ");
	}
	else {
		console_WFC_thread->info("Json file open successfully! ");
		rlogger_WFC_thread->info("Json File open successfully! ");
	}


	//QString strInput = ui.textEdit_input->toPlainText();
	//strUrl += "http://" + ui.lineEdit_Url->text();
	strUrl = serverIP + serviceID_get;
	strInput = clientSecret;
	SendAndGetText(strUrl, get1, strInput, strMessage, strResult);

	QJsonParseError jsonError;
	QByteArray byteResult = strResult.toUtf8();//QString转QByteArray
											   //QJsonDocument json = QJsonDocument::fromJson(byteResult, &jsonError);
	json_doc_get = QJsonDocument::fromJson(byteResult, &jsonError);

	if (json_doc_get.isNull())
	{
		console_WFC_thread->critical("json文件为空文档");
		//console->critical("jsonError错误类型" + jsonError.errorString().toStdString());
		//qDebug() << jsonError.errorString();
		rlogger_WFC_thread->critical("json格式错误[json_doc_get_slot]");
		//qDebug() << "json格式错误";//注意:如果文档中有中文,就会报格式错误
	}
	else if (jsonError.error != QJsonParseError::NoError)
	{
		console_WFC_thread->critical("json格式错误[json_doc_get_slot]" + jsonError.errorString().toStdString());
		//qDebug() << jsonError.errorString();
		rlogger_WFC_thread->critical("json格式错误[json_doc_get_slot]");
	}
	else 
	{
		if (json_doc_get.isObject()) 
		{
			doc_get_Obj = json_doc_get.object();
		}
	}
	json_doc_get_parse();
//	RT_Iplat4j_Communication();  //放在这里可以在主窗口点击数据读取按钮之后,才会提示是否数据平台连接成功

	file.write(json_doc_get.toJson(QJsonDocument::Indented)); //Indented:表示自动添加/n回车符
	file.close();

}

void Zanj_WFC_ctrl::OnconfigSlot()
{
	configuration_read();
}

         Zanj_WFC_ctrl.h文件

        该文件也是.cpp文件中众多函数的声明,比如处理读取数据时发送的信号的函数需要在这里声明一下,便于使用。

#pragma once

#include <QObject>
#include <QtSql/QtSql>
#include <QVariant>

//#include <connection.h>
//#include <QSql>
#include <QSqlError>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QDebug>
#include <QMessageBox>
#include <QEventLoop>
#include <qstring.h>
#include <string>
#include "snap7.h"
#include <QtCore/QJsonObject>
#include <QtCore/QJsonDocument>
#include <Python.h>


class Zanj_WFC_ctrl : public QObject
{
	Q_OBJECT

	
public:
	Zanj_WFC_ctrl(QObject *parent=0);
	~Zanj_WFC_ctrl();

public slots:

	int HDGL_SYSTEM_Init();

	void OnGETSlot();
	
	void OnconfigSlot();
	

总结

     同时运行的两个线程,与单线程相比:

        提高系统性能:双线程可以并行执行,可以更充分地利用多核处理器的资源,从而提高系统的整体性能。

        增加程序响应速度:通过将一些耗时的操作放在单独的线程中执行,可以避免阻塞主线程,提高程序的响应速度和用户体验。

        提高系统的并发性:双线程可以同时处理不同的任务,增加系统的并发性,使系统更具有高效性和灵活性。

        分解复杂任务:通过将复杂任务拆分成多个线程来执行,可以更容易地管理和维护代码,提高代码的可读性和可维护性。

        改善软件的稳定性:通过将不同功能模块分别放在不同的线程中执行,可以避免由于一个线程的错误导致整个系统崩溃的情况,提高软件的稳定性。

        总的来说,双线程可以提高系统的性能、响应速度和并发性,同时也能改善软件的稳定性和可维护性,是一种常用的提升程序效率的方法。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

软件物料清单(SBOM)生成指南 .pdf

如今软件安全攻击技术手段不断升级&#xff0c;攻击数量显著增长。尤其是针对软件供应链的安全攻击&#xff0c;具有高隐秘性、追溯难的特点&#xff0c;对企业软件安全威胁极大。 同时&#xff0c;软件本身也在不断地更新迭代&#xff0c;软件内部成分安全性在持续变化浮动。…

web题目实操 5(备份文件和关于MD5($pass,true)注入的学习)

1.[ACTF2020 新生赛]BackupFile &#xff08;1&#xff09;打开页面后根据提示是备份文件 &#xff08;2&#xff09;查看源码发现啥都没有 &#xff08;3&#xff09;这里啊直接用工具扫描&#xff0c;可以扫描到一个文件名为&#xff1a;/index.php.bak的文件 &#xff08;…

json解析大全

JSON解析案例1 将String转为JSONObject JSONObject res JSONObject.parseObject(result);获取documents JSONArray array res.getJSONObject("result").getJSONArray("documents");遍历JSONArray for (int i 0; i < array.size(); i) {JSONObject…

IDEA pom.xml依赖警告

IDEA中&#xff0c;有时 pom.xml 中会出现如下提示&#xff1a; IDEA 2022.1 升级了检测易受攻击的 Maven 和 Gradle 依赖项&#xff0c;并建议修正&#xff0c;通过插件 Package Checker 捆绑到 IDE 中。 这并不是引用错误&#xff0c;不用担心。如果实在强迫症不想看到这个提…

稳态视觉诱发电位 (SSVEP) 分类学习系列 (4) :Temporal-Spatial Transformer

稳态视觉诱发电位分类学习系列:Temporal-Spatial Transformer 0. 引言1. 主要贡献2. 提出的方法2.1 解码的主要步骤2.2 网络的主要结构 3. 结果和讨论3.1 在两个数据集下的分类效果3.2 与基线模型的比较3.3 消融实验3.4 t-SNE 可视化 4. 总结欢迎来稿 论文地址&#xff1a;http…

Hive——DDL(Data Definition Language)数据定义语句用法详解

1.数据库操作 1.1创建数据库 CREATE DATABASE [IF NOT EXISTS] database_name [COMMENT database_comment] [LOCATION hdfs_path] [WITH DBPROPERTIES (property_nameproperty_value, ...)];IF NOT EXISTS&#xff1a;可选参数&#xff0c;表示如果数据库已经存在&#xff0c…

软考-系统分析师-精要2

5、可行性分类 经济可行性&#xff1a;成本收益分析&#xff0c;包括建设成本、运行成本和项目建设后可能的经济收益。 技术可行性&#xff1a;技术风险分析&#xff0c;现有的技术能否支持系统目标的实现&#xff0c;现有资源&#xff08;员工&#xff0c;技术积累&#xff0…

GEM TSU Interface Details and IEEE 1588 Support

摘要&#xff1a;Xilinx ZNYQ ULTRASCALE MPSOC的GEM和1588的使用 对于FPGA来说&#xff0c;只需要勾选一些znyq的配置就行了&#xff0c;其余的都是软件的工作&#xff1b; 所有配置都勾选之后&#xff0c;最终会露出来的接口如下&#xff1a; GEM需要勾选的配置如下&#xf…

泰坦尼克号乘客生存情况预测分析2

泰坦尼克号乘客生存情况预测分析1 泰坦尼克号乘客生存情况预测分析2 泰坦尼克号乘客生存情况预测分析3 泰坦尼克号乘客生存情况预测分析总 背景描述 Titanic数据集在数据分析领域是十分经典的数据集&#xff0c;非常适合刚入门的小伙伴进行学习&#xff01; 泰坦尼克号轮船的…

AI新闻速递:揭秘本周科技界最热的AI创新与发展

兄弟朋友们&#xff0c;本周的AI领域又迎来了一系列激动人心的进展。在这个快速变化的时代&#xff0c;不会利用AI的人&#xff0c;就像在数字化高速公路上步行的旅行者&#xff0c;眼看着同行者驾驶着智能汽车绝尘而去&#xff0c;而自己却束手无策。 1. Adobe Firefly 3&…

【基础算法总结】双指针算法二

双指针 1.有效三角形的个数2.和为S的两个数字3.和为S的两个数字4.四数之和 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f603; 1.有效三角形的个数…

深度学习运算:CUDA 编程简介

一、说明 如今&#xff0c;当我们谈论深度学习时&#xff0c;通常会将其实现与利用 GPU 来提高性能联系起来。GPU&#xff08;图形处理单元&#xff09;最初设计用于加速图像、2D 和 3D 图形的渲染。然而&#xff0c;由于它们能够执行许多并行操作&#xff0c;因此它们的实用性…

Python游戏工具包pygame

当你涉及游戏开发时&#xff0c;Pygame是一个强大的工具包&#xff0c;它提供了一系列功能丰富的模块和工具&#xff0c;让你可以轻松地创建各种类型的游戏。在本文中&#xff0c;我将介绍Pygame的依赖以及其详细属性&#xff0c;同时提供一些示例代码来说明其用法。 目录 一…

Github 2024-04-27 开源项目日报 Top9

根据Github Trendings的统计,今日(2024-04-27统计)共有9个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目6TypeScript项目2C++项目1JavaScript项目1Open-Sora: 构建自己的视频生成模型 创建周期:17 天开发语言:Python协议类型:Apache Lic…

嵌入式Linux学习——Linux常用命令(上)

Linux命令行介绍 Linux Shell 简介 Shell 的意思是“外壳”&#xff0c;在 Linux 中它是一个程序&#xff0c;比如/bin/sh、/bin/bash 等。它负责接收用户的输入&#xff0c;根据用户的输入找到其他程序并运行。比如我们输入“ ls”并回车时&#xff0c; shell 程序找到“ ls…

TinyML之Hello world----基于Arduino Nano 33 BLE Sense Rev2的呼吸灯

早期版本的Hello World 这应该是一个逼格比较高的呼吸灯了&#xff0c;用ML来实现呼吸灯功能&#xff0c;之前已经有大佬发过类似的文章&#xff1a;https://blog.csdn.net/weixin_45116099/article/details/126310816 当前版本的Hello World 这是一个ML的入门例程&#xff…

黑马程序员C++学习总结【进阶篇】

本阶段主要针对C泛型编程和STL技术做详细讲解&#xff0c;探讨C更深层的使用 黑马程序员C学习总结【基础篇】 黑马程序员C学习总结【核心篇】 黑马程序员C学习总结【进阶篇】 黑马程序员C学习总结【进阶篇】 一、模板1.函数模板&#xff08;1&#xff09;函数模板2种使用方式&a…

重学java 25.面向对象 权限修饰符、final关键字、代码块

别让平淡生活&#xff0c;耗尽你所有的向往 —— 24.4.27 重点概述 01.知道final修饰成员之后特点 02.会使用静态代码块以及知道静态代码块的使用场景 03.会使用匿名内部类 一、权限修饰符 1.概述 在Java中提供了四种访问权限&#xff0c;使用不同的访问权限修饰符修饰时&#…

为什么 Facebook 不使用 Git?

在编程的世界里&#xff0c;Git 就像水一样常见&#xff0c;以至于我们认为它是创建和管理代码更改的唯一可行的工具。 前 Facebook 员工&#xff0c;2024 年 首先&#xff0c;我为什么关心&#xff1f; 我致力于构建 Graphite&#xff0c;它从根本上受到 Facebook 内部工具的…

第十五届蓝桥杯省赛第二场C/C++B组E题【遗迹】题解

解题思路 错解 贪心&#xff1a;每次都移动至当前最近的对应方块上。 反例&#xff1a; s s s abxac t t t abac 贪心结果&#xff08;下标&#xff09; 0 → 1 → 0 → 4 0 \rightarrow 1 \rightarrow 0 \rightarrow 4 0→1→0→4&#xff0c;答案为 5 5 5。 正确结…