时钟
头文件
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QPaintEvent>
#include <QTimer>
#include <QPainter>
#include <QPen>
#include <QBrush>
#include <QTime>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void paintEvent(QPaintEvent *event) override;
public slots:void clock_slot();
private:Ui::Widget *ui;QTimer *timer;int hour;int minute;int second;int count = 0;
};
#endif // WIDGET_H
源文件
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);this->setFixedSize(800,800);timer = new QTimer(this); //定义一个计时器timer->start(1000); //启动计时器connect(timer,&QTimer::timeout,this,&Widget::clock_slot); //连接每秒执行的槽函数QString t = QTime::currentTime().toString("h:m:s ap"); //获取时间QStringList list1 = t.split(" "); //分割时间后的am/pmQStringList list2 = list1[0].split(":"); //分割时间中的:hour = list2[0].toInt(); //获取小时minute = list2[1].toUInt(); //获取分钟second = list2[2].toUInt(); //获取秒钟
}Widget::~Widget()
{delete ui;
}void Widget::paintEvent(QPaintEvent *event)
{QPainter p(this); //设置画笔格式QPen pen(QColor("blue"));pen.setWidth(5);QBrush b("yellow");p.setPen(pen);p.setBrush(b);//把画家移动到中间p.translate(this->width()/2,this->height()/2);p.drawEllipse(QPoint(0,0),200,200); //画圆pen.setColor(QColor("black"));p.setPen(pen);for(int i=0;i<60;i++) //绘制分钟线{p.rotate(6);p.drawLine(QPoint(200,0),QPoint(195,0));}pen.setWidth(10);p.setPen(pen);for(int i=0;i<12;i++) //绘制时钟线{p.drawLine(QPoint(200,0),QPoint(190,0));p.rotate(30);p.drawText(QPoint(0,-170),QString("%1").arg(i+1));}//时针pen.setWidth(10);pen.setColor(QColor("red"));p.setPen(pen);p.rotate(hour*30+6*second/60/12+30*minute/60+6*count/60/12);p.drawLine(QPoint(0,-50),QPoint(0,5));//分针QPainter p1(this);p1.translate(this->width()/2,this->height()/2);pen.setWidth(6);pen.setColor(QColor("green"));p1.setPen(pen);p1.rotate(6*count/60+minute*6+6*second/60);p1.drawLine(QPoint(0,-80),QPoint(0,8));//秒针QPainter p2(this);p2.translate(this->width()/2,this->height()/2);pen.setWidth(3);pen.setColor(QColor("pink"));p2.setPen(pen);p2.rotate(6*count+6*second);p2.drawLine(QPoint(0,-120),QPoint(0,12));
}void Widget::clock_slot()
{count++;this->update();
}
效果图
QTTCP服务器客户端
客户端
头文件
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QTcpSocket> //客户端套接字类型
#include <QMessageBox> //消息对话框类
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_connectButton_clicked();void connected_slot();void readyRead_slot();void on_sendButton_clicked();void on_pushButton_clicked();void disconnected_slot();
private:Ui::Widget *ui;//定义通信用的变量QTcpSocket *client; //定义套接字指针QString usrName; //用户名
};
#endif // WIDGET_H
源文件
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//实例化客户端对象client = new QTcpSocket(this);//当客户端和服务器建立联系后,如果客户端接收到服务器发来的消息//客户端自身就会自动发射一个readyRead的信号,我们可以将connect(client,&QTcpSocket::readyRead,this,&Widget::readyRead_slot);//当客户端断开了与服务器的连接后,该客户端就会自动发射一个disconnected的信号//我们可以将该信号连接到自定义的槽函数中处理相关逻辑connect(client,&QTcpSocket::disconnected,this,&Widget::disconnected_slot);//如果当前客户端成功连接服务器,那么该客户端就会发射一个connected的信号//我们可以将该信号连接到自定义的槽函数中处理相关逻辑connect(client,&QTcpSocket::connected,this,&Widget::connected_slot);
}Widget::~Widget()
{delete ui;
}//连接服务器按钮对应的槽函数
void Widget::on_connectButton_clicked()
{//获取ui界面上的数据QString ip = ui->iplineEdit->text(); //ip地址quint16 port = ui->portlineEdit->text().toInt(); //端口号usrName = ui->namelineEdit->text();//调用套接字成员函数,连接服务器//函数原型:connectToHost(const QHostAddress &address, quint16 port, OpenMode mode = ReadWrite);//参数1:要被的服务器ip地址//参数2:服务器的端口号//参数3:默认为可读可写//返回值:无client->connectToHost(ip,port);
}//处理connected信号的槽函数的定义
void Widget::connected_slot()
{QMessageBox::information(this,"成功","连接成功");//将相关组件禁用ui->iplineEdit->setEnabled(false);ui->portlineEdit->setEnabled(false);ui->namelineEdit->setEnabled(false);//向服务器发送一条消息QString msg = usrName + "进入聊天室";client->write(msg.toLocal8Bit());
}//自定义处理readyRead信号的槽函数
void Widget::readyRead_slot()
{//从套接字中读取数据QByteArray msg = client->readAll();ui->msglistWidget->addItem(QString::fromLocal8Bit(msg));
}//消息发送按钮对应的槽函数
void Widget::on_sendButton_clicked()
{//组织要发送的消息QString msg = usrName + ":" + ui->msglineEdit->text();//将消息发送给服务器client->write(msg.toLocal8Bit());//将消息展示到自己界面上//准备一个QListWidgetItem类的对象QListWidgetItem *item = new QListWidgetItem(msg);item->setTextAlignment(Qt::AlignRight); //将文本右对齐ui->msglistWidget->addItem(item);//清空消息发送框的内容ui->msglineEdit->clear();
}//断开连接按钮对应的槽函数
void Widget::on_pushButton_clicked()
{//执行断开连接的操作//准备发送消息给服务器QString msg = usrName + ": 离开聊天室";client->write(msg.toLocal8Bit());//断开连接client->disconnectFromHost();
}//自定义处理disconne信号对应的槽函数
void Widget::disconnected_slot()
{QMessageBox::information(this,"提示","成功断开与服务器的连接");//将相关组件启用ui->iplineEdit->setEnabled(true);ui->portlineEdit->setEnabled(true);ui->namelineEdit->setEnabled(true);ui->sendButton->setEnabled(true);
}
服务器
头文件
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QTcpServer> //tcp服务器类
#include <QTcpSocket> //tcp客户端类
#include <QList> //链表类
#include <QMessageBox> //消息对话框类
#include <QDebug> //信息调制类
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_startbtn_clicked();void newConnection_slot();//自定义void readReady_slot();//自定义处理readyRead信号的槽函数private:Ui::Widget *ui;QTcpServer *server; //定义服务器指针QList<QTcpSocket *> socketlist;//定义存放客户端信息的容器
};
#endif // WIDGET_H
源文件
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//实例化一个服务器对象server = new QTcpServer(this);}Widget::~Widget()
{delete ui;
}//启动服务器按钮对应的槽函数
void Widget::on_startbtn_clicked()
{if(ui->startbtn->text() == "启动服务器"){//执行启动服务器的操作//获取ui界面上的端口号quint16 port = ui->portEdit->text().toUInt();//if(!server->listen(QHostAddress::Any,port)){QMessageBox::critical(this,"错误","服务器启动失败");return;}//程序执行至此,表示服务器启动成功QMessageBox::information(this,"成功","服务器启动成功");//将行编辑器设置为不可用ui->portEdit->setEnabled(false);//将按钮文本内容设置为关闭服务器ui->startbtn->setText("关闭服务器");//此时如果有客户端发来连接请求,那么该服务器就会自动发射一个newConnection的洗脑//我们可以将该信号连接到自定义的槽函数中,处理后续操作connect(server,&QTcpServer::newConnection,this,&Widget::newConnection_slot);}else{//执行关闭服务器的动作server->close();//socketlist.removeAll();socketlist.clear();//将行编辑器设置成可用状态ui->portEdit->setEnabled(true);//将按钮文本设置为启动服务器ui->startbtn->setText("启动服务器");}
}//自定义处理newConnection信号的槽函数的实现
void Widget::newConnection_slot()
{qDebug()<<"有新的客户端发来请求了";//可以通过成员函数 nextPendingConnection函数获取最新连接的客户端套接字的地址//函数原型:QTcpSocket *nextPendingConnection();//无参函数//返回值:最新的一个连接的套接字地址QTcpSocket *s = server->nextPendingConnection();//将该套接字放入客户端链表中socketlist.push_back(s);//程序执行至此,一个服务器可以对应多个客户端,已经建立了连接//此时,如果有某个客户端发来数据,那么该客户端套接字就会自动发送一个readyRead的信号//我们可以将该信号连接到自定义的槽函数中处理相关逻辑connect(s,&QTcpSocket::readyRead,this,&Widget::readReady_slot);
}//自定义处理readyRead信号的槽函数的实现
void Widget::readReady_slot()
{//1.遍历链表中的所有客户端,如果客户端的状态为未连接,则直接移除出链表//函数原型:SocketState state() const;//功能:返回当前套接字的状态//返回结果为0时,表示该套接字时未连接状态for(int i=0;i<socketlist.size();i++){//判断当前套接字,socketList[i]是否失效if(socketlist[i]->state() == 0){//将该套接字移除链表socketlist.removeAt(i);}}//2.遍历所有客户端,判断客户端中是否有数据可读,如果有数据可读,则表示是该客户端发来的消息for(int i=0;i<socketlist.count();i++){//判断当前客户端中是否有数据可读//函数原型:qint64 bytesAvailable() const override;//参数无//返回值:返回当前客户端套接字中的待读数据,如果没有数据,则返回0if(socketlist[i]->bytesAvailable() != 0){//读取当前套接字的内容QByteArray msg = socketlist[i]->readAll();//将接收的消息展示到ui界面上ui->msglistWidget->addItem(QString::fromLocal8Bit(msg));//将收到的消息,全部发送给其他客户端for(int j=0;j<socketlist.length();j++){if(i!=j) //防止自己发给自己{socketlist[j]->write(msg);}}}}
}
指针和引用的区别
1> 指针定义时需要使用*号,引用定义时需要使用&
2> 指针取值需要使用*号运算符完成,引用使用时直接跟目标使用方式一致
3> 指针定义时,需要给指针分配内存空间8字节,引用定义时不需要分配内存空间,引用使用的是目标的空间
4> 指针初始化后,可以改变指针的指向,但是引用初始化后,不能在改变目标了
5> 指针有二级指针,但是引用没有二级引用
6> 有空指针,但是没有空引用
7> 指针进行偏移运算时是对内存地址的偏移,而引用进行偏移时,就是对目标值的偏移
8> 指针不能指向右值,但是右值引用的目标可以是右值
9> 指针定义时可以不初始化(野指针),引用定义时必须初始化
10> 指针可以有指针数组,但是引用不能定义引用数组
C/C++的区别
C 语言是一种面向过程的编程语言
C++ 是一种面向对象的编程语言,同时也支持面向过程编程。它引入了类、对象、封装、继承、多态等面向对象的特性
Qt中信号与槽机制
1> 在对象树模型中,子组件构造时,需要指定父组件而存在
2> 子组件的生命周期由父组件进行管理
3> 当父组件展示时,会将子组件一并展示,当父组件释放空间时,会将加载在该组件上的所有子组件的空间全部释放
4> 对象树模型保证了,各个组件的内存不会泄露
5> 在栈区申请的组件,程序结束后,由系统自动回收,在堆区申请的组件,结束后,由其父组件进行回收资源
6> 父组件和子组件要用于共同的祖先类,实现的理论基础为多态
7> 每一个组件中都会拥有一个父组件指针和一个子组件链表
Qt对象树模型
所谓信号,就是信号函数,定义在类的signals权限下,是一个不完整的函数,只有函数声明,没有函数定义。返回值类型为void
信号函数不能当作普通函数一样被调用,只能被发射出去。
所谓槽,就是槽函数,定义在类的slots权限,是一个完整的函数,既有函数声明,也有函数定义,返回值类型为void
槽函数可以当作普通函数一样被调用,但是普通函数不能当作槽函数接收信号
什么情况需要多线程
在程序中因为io阻塞不能执行下一个任务时,可以使用多线程来执行另外的任务,不受到主线程io阻塞的影响而不能执行任务
多线程使用需要注意什么
临时资源被访问时因为时间片轮询机制,正在访问临时资源的线程的时间片用完了,在别的线程中访问了临时资源并修改了临时资源导致该线程再次访问临时资源是数据收到了篡改。
注意死锁问题,在使用多个同步机制时,如果线程获取锁的顺序不当,可能会导致死锁,比如一个线程所需要的资源在另一个线程中,要等另一个线程释放锁才能进行资源访问
epoll,select,poll区别
select
和poll
的编程相对较为简单直接,但在处理大量文件描述符时,代码会变得较为繁琐。需要手动遍历文件描述符集合来确定哪些文件描述符有事件发生,并且需要处理不同类型的事件(可读、可写、异常)。
epoll
的编程相对复杂一些,需要使用特定的函数(如epoll_create
、epoll_ctl
、epoll_wait
)来进行操作。但是,由于其高效的事件通知方式和对大量文件描述符的良好支持,在复杂的网络编程场景中,使用epoll
可以使代码更加清晰和易于维护。
智能指针
申请了内存空间,使用后忘记释放内存空间,堆区对象没有得到析构而导致内存泄露。
通过只能指针托管堆区空间可以在程序结束时,通过智能指针的析构函数自动释放空间
分为独占智能指针,共享智能指针,弱智能指针
共享内存和消息队列区别
共享内存需要进程自己进行同步和数据一致性的控制。由于多个进程可以同时访问共享内存,可能会出现数据竞争和不一致的情况。因此,进程需要使用同步机制如互斥锁、信号量等来确保对共享内存的正确访问。
消息队列由内核进行管理,通常提供了一定程度的同步机制。发送和接收消息的操作是原子性的,保证了消息的完整性和顺序性。但是,如果多个进程同时从一个消息队列中接收消息,可能会出现竞争,需要额外的同步措施。