目录
1.多态,虚函数,纯虚函数
1.多态性(Polymorphism):
2.虚函数(Virtual Function):
3.纯虚函数(Pure Virtual Function):
将引用作为函数参数有以下几个特点
1.传递引用可以避免拷贝:
2.可以修改原始对象:
3.引用参数需要传递有效对象:
4.可以避免拷贝构造函数的调用:
5.对原始对象的修改在函数外可见:
结构和联合之间的主要区别:
1.内存布局:
2.成员访问:
3.占用空间:
4.用途:
练习一个界面加跳转到另一个界面
界面1 .h文件
界面2 .h文件
页面2 form.cpp 文件
页面1 widget.cpp 文件
main.cpp 文件
结果为:
一、加载资源文件
1> 将所需的资源文件夹放入工程项目所在路径下
2> 在工程文件中,右键添加新文件
3> 选择Qt --》资源文件
4> 添加前缀
5> 添加文件
6> 点下锤子即可
二、信号与槽机制(非常重要)
2.1 信号与槽的介绍
2.2 信号与槽的连接
2.3 发射自定义信号
2.4 信号与槽的案例
2.5 信号函数与槽函数的总结
2.6 使用信号与槽完成两个界面的跳转
多态性(Polymorphism),虚函数(Virtual Function),以及纯虚函数(Pure Virtual Function)都是面向对象编程中的重要概念,用于实现对象之间的灵活性和可扩展性。
多态性指的是同一个接口可以被不同的对象以不同的方式实现。这使得不同的类可以共享相同的接口,但可以根据其具体实现在运行时表现出不同的行为。多态性有两种类型:编译时多态性(静态多态性)和运行时多态性(动态多态性)。在C++中,运行时多态性是通过虚函数实现的。
虚函数是在基类中声明为虚拟的,并在派生类中进行重写(覆盖)的函数。通过使用虚函数,可以在运行时根据对象的实际类型来调用相应的函数。使用虚函数,可以实现运行时多态性,使得通过基类指针或引用调用函数时,能够调用到派生类的实现。
class Shape {
public:
virtual void draw() {
// 基类虚函数
}
};
class Circle : public Shape {
public:
void draw() override {
// 派生类覆盖基类虚函数
}
};
纯虚函数是在基类中声明为虚拟的,但没有在基类中给出实现的函数。它的目的是要求派生类提供实现,否则派生类也会成为抽象类。一个包含纯虚函数的类被称为抽象基类(Abstract Base Class)。
class AbstractShape {
public:
virtual void draw() = 0; // 纯虚函数
};
class ConcreteCircle : public AbstractShape {
public:
void draw() override {
// 派生类必须提供纯虚函数的实现
}
};
总之,多态性通过虚函数和纯虚函数的概念实现,使得面向对象的程序能够更加灵活地适应不同的场景和需求,同时提高了代码的可扩展性和可维护性。
与传递对象本身相比,传递引用作为函数参数可以避免在函数调用过程中进行对象的拷贝操作,从而提高性能和效率,特别是对于大对象或类对象来说。
通过传递引用,函数可以直接修改传递的对象,因为引用是原对象的别名。这使得函数能够对对象进行原地修改,而不需要返回修改后的对象。
传递引用参数时,需要确保传递的是有效的对象,因为引用不能指向空值(nullptr)。如果传递了无效的对象引用,可能会导致未定义的行为。
当传递对象的副本时,会调用拷贝构造函数来创建一个新的对象,但传递引用参数时不会触发拷贝构造函数的调用。
由于传递的是原始对象的引用,对对象的修改会在函数外部可见,不像传值参数那样只是在函数内部有效。以下是一个使用引用作为函数参数的示例:
#include <iostream>
void modifyValue(int &x) {
x = x * 2;
}
int main() {
int num = 5;
std::cout << "Original value: " << num << std::endl;
modifyValue(num);
std::cout << "Modified value: " << num << std::endl;
return 0;
}
在这个示例中,modifyValue 函数接受一个整数引用作为参数,直接修改传递的整数值。在 main 函数中,调用 modifyValue 后,原始的 num 值被修改,这种修改在函数外部可见。
结构(struct):结构中的每个成员都在内存中占据独立的空间,成员之间不会共享内存。这意味着结构的大小至少是其所有成员大小之和。
联合(union):联合中的所有成员共享同一块内存,联合的大小取决于最大成员的大小。只有联合中的一个成员可以被有效地存储。
结构(struct):可以同时访问结构中的多个成员,每个成员都有自己的地址。
联合(union):只能访问联合中当前活动的成员,因为它们共享相同的内存空间。
结构(struct):占用的内存空间取决于所有成员的大小之和,每个成员都有自己的内存空间。
联合(union):占用的内存空间取决于最大成员的大小,不同成员共享同一块内存。
结构(struct):用于存储一组不同类型的数据,每个成员都有自己的含义。
联合(union):用于在同一块内存中存储不同类型的数据,但只会使用其中一个成员。下面是一个简单的示例,说明结构和联合之间的区别:
#include <iostream>
struct MyStruct {
int a;
double b;
};
union MyUnion {
int x;
double y;
};
int main() {
MyStruct s;
MyUnion u;
std::cout << "Size of struct: " << sizeof(s) << std::endl; // Output: Size of struct: 12
std::cout << "Size of union: " << sizeof(u) << std::endl; // Output: Size of union: 8
s.a = 5;
s.b = 3.14;
u.x = 10;
u.y = 2.71;
std::cout << "Struct members: " << s.a << ", " << s.b << std::endl; // Output: Struct members: 5, 3.14
std::cout << "Union members: " << u.x << ", " << u.y << std::endl; // Output: Union members: 2, 2.71
return 0;
}
需要根据具体的需求来选择使用结构还是联合。如果需要在同一块内存中存储不同类型的数据,而且每次只会使用其中一个成员,那么联合可能更合适。如果需要同时存储多个成员,每个成员都有自己的含义,那么结构更适合。
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include
#include
#include
#include
#include
class Widget : public QWidget
{
Q_OBJECT //信号与槽的元对象
signals:
//自定义信号函数
void jump();
//void btn_1_clicked();
public slots:
//自定义的槽函数
void btn_1_clicked();
void btn_2_clicked();
//void on_jump_slot();
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
QLabel *lab2;
QLabel *lab3;
QLabel *lab1;
QLineEdit *edit1;
QLineEdit *edit2;
QPushButton *btn1;
QPushButton *btn2;
};
#endif // WIDGET_H
#ifndef FORM_H
#define FORM_H
#include
namespace Ui {
class Form;
}
class Form : public QWidget
{
Q_OBJECT
public:
explicit Form(QWidget *parent = nullptr);
~Form();
public slots:
void jump_slot();
private:
Ui::Form *ui;
};
#endif // FORM_H
#include "form.h"
#include "ui_form.h"
Form::Form(QWidget *parent) :
QWidget(parent),
ui(new Ui::Form)
{
ui->setupUi(this);
}
Form::~Form()
{
delete ui;
}
void Form::jump_slot()
{
this->show();
}
#include "widget.h"
#include "form.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
this->setFixedSize(900, 800); //设置固定尺寸
//3、窗口标题
this->setWindowTitle("My First Window");
//4、设置窗口的icon
this->setWindowIcon(QIcon(":/new/prefix1/icon/wodepeizhenshi.png"));
//5、设置背景色,一般使用样式表完成
this->setStyleSheet("background-color:rgb(226,227,228);");
//标签
lab1 = new QLabel(this);
lab1->setGeometry(0, 0, 900, 458);
lab1->setPixmap(QPixmap(":/new/prefix1/icon/logo1.png"));
lab1->setScaledContents(true);
lab2 = new QLabel(this);
lab2->setGeometry(270, 500, 40, 40);
lab2->setPixmap(QPixmap(":/new/prefix1/icon/userName.jpg"));
lab2->setScaledContents(true);
lab3 = new QLabel(this);
lab3->setGeometry(270, 580, 40, 40);
lab3->setPixmap(QPixmap(":/new/prefix1/icon/passwd.jpg"));
lab3->setScaledContents(true);
//行编辑器
//1、构造一个行编辑器,构造时给定父组件
edit1
= new QLineEdit(this);
edit2 = new QLineEdit(this);
edit1->setPlaceholderText("QQ/手机/邮箱"); //设置编辑器的占位文本
edit2->setPlaceholderText("密码");
edit1->resize(200,40); //设置尺寸
edit2->resize(200,40);
edit1->move(350,500); //移动位置
edit2->move(350,580);
edit2->setEchoMode(QLineEdit::Password); //设置回显模式
//按钮组件
btn1 = new QPushButton("登录", this);
btn2 = new QPushButton("取消", this);
btn1->resize(110,60);
btn1->move(320,650);
btn2->resize(110,60);
btn2->move(450,650);
btn1->setIcon(QIcon(":/new/prefix1/icon/login.png"));
btn2->setIcon(QIcon(":/new/prefix1/icon/cancel.png"));
//使用qt5版本连接
connect(btn1,&QPushButton::clicked,this,&Widget::btn_1_clicked);
//使用qt4版本连接
connect(btn2,SIGNAL(pressed()),this,SLOT(close()));
}
Widget::~Widget()
{
}
void Widget::btn_1_clicked()
{
if(edit1->text() == "admin" && edit2->text() == "123456")
{
qDebug() << "登录成功";
emit jump();
this->close();
}
else
{
qDebug() << "登录失败";
edit1->clear();
edit2->clear();
}
}
void Widget::btn_2_clicked()
{
this->close();
}
#include "widget.h"
#include "form.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
Form m;
QObject::connect(&w,&Widget::jump ,&m,&Form::jump_slot);
return a.exec();
}
1> 功能:实现多个组件之间的互相通信,是QT引以为傲的核心机制
2> 信号:就是信号函数,定义在类体的signals权限下,是一个不完整的函数,只有声明没有定义
3> 槽:就是槽函数,定义在类体的slots权限下,是一个完整的函数,既有声明,也有定义,也可以当做普通函数被使用
4> 无论是信号函数还是槽函数,返回值一般都是void类型,参数可以自定义,参数的目的就是为了传递信息的
5> 包含信号与槽定义的类
class Widget : public QWidget
{
Q_OBJECT //信号与槽的元对象
signals:
void my_signal(); //自定义信号函数
public slots:
void my_slot(); //自定义的槽函数
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
};
1> 在ui界面下的信号与槽的编辑处进行连接,该连接方式只能发射系统提供的信号,并使用系统提供的槽函数接受
2> 在ui界面的组件上,右键转到槽,选择信号,槽函数逻辑自己书写
3> qt4版本的连接函数,该版本的连接是不友好的连接
[static] QMetaObject::Connection //返回值是一个连接,并且该函数是一个静态成员函数
QObject::connect( //函数名
const QObject *sender, //信号的发射者,是组件的指针
const char *signal, //要发射的信号是一个C语言风格的字符串,将信号函数传递进来时,需要使用SIGNAL宏进行转换
const QObject *receiver, //信号的接受者,是组件的指针
onst char *method) //处理信号的槽函数,是C风格字符串,将槽函数传递进来时,需要使用SLOT宏进行转换
注意:信号函数必须是信号的发射者所在的类中有的信号函数,而槽函数也必须是信号的接受者中有的槽函数
举个例子:
QLabel *label = new QLabel;
QScrollBar *scrollBar = new QScrollBar;
QObject::connect(scrollBar, SIGNAL(valueChanged(int)),
label, SLOT(setNum(int)));
4> qt5版本的连接函数
[static] QMetaObject::Connection //返回值是一个连接,并且该函数是一个静态成员函数
QObject::connect( //函数名
const QObject *sender, //信号的发射者,是组件的指针
PointerToMemberFunction signal, //信号函数的函数指针变量,直接填写函数名即可
const QObject *receiver, //信号的接受者,是组件的指针
PointerToMemberFunction method) //槽函数的函数指针变量,直接填写函数名即可
举个例子:
QLabel *label = new QLabel;
QLineEdit *lineEdit = new QLineEdit;
QObject::connect(lineEdit, &QLineEdit::textChanged,
label, &QLabel::setText);
5> 信号连接外部函数或lambda表达式
[static] QMetaObject::Connection //返回值是一个连接,并且该函数是一个静态成员函数
QObject::connect( //函数名
const QObject *sender, //信号的发射者,是组件的指针
PointerToMemberFunction signal, //信号函数的函数指针变量,直接填写函数名即可
Functor functor) //处理信号的功能函数,可以是全局函数,也可以是lambda表达式
举个例子:
void someFunction();
QPushButton *button = new QPushButton;
QObject::connect(button, &QPushButton::clicked, someFunction);
Lambda 表达式作为槽函数:
QByteArray page = ...;
QTcpSocket *socket = new QTcpSocket;
socket->connectToHost("qt-project.org", 80);
QObject::connect(socket, &QTcpSocket::connected, [=] () {
socket->write("GET " + page + "\r\n");
});
6> 断开信号与槽的连接
只需将上面的连接函数前加dis即可,参数都不变
//断开按钮对应的槽函数
void Widget::on_btn6_clicked()
{
disconnect(btn3, SIGNAL(pressed()), ui->btn1, SLOT(hide()));
disconnect(ui->btn4, &QPushButton::clicked, this, &Widget::my_slot);
}
1> 在自定义类体内的signals权限下,定义自定义的信号函数,返回值为void,可以有参数也可以没有参数
2> 在程序所需处,使用关键字emit发射自定义的信号:emit 信号名(实参列表);
3> 将自定义的信号与槽函数进行连接
4> 当自定义的信号发射后,槽函数会立即响应
1> 配置文件
2> 头文件
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include
#include //文本转语音的头文件
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT //信号与槽的元对象
signals:
void my_signal(); //自定义信号函数
public slots:
void my_slot(); //自定义的槽函数
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_btn2_clicked();
void on_btn6_clicked();
private:
Ui::Widget *ui;
//自定义一个按钮
QPushButton *btn3;
//定义一个播报员指针
QTextToSpeech *speecher;
};
#endif // WIDGET_H
3> 源文件
#include "widget.h"
#include "ui_widget.h"
void fun()
{
qDebug()<<"我是过路的";
}
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//给播报员实例化空间
speecher = new QTextToSpeech(this);
//给btn3实例化空间
this->btn3 = new QPushButton("按钮3", this);
this->btn3->move(ui->btn2->x(), ui->btn2->y()+50);
this->btn3->resize(ui->btn2->size());
//使用qt4版本的连接,将按钮3发射的pressed信号与按钮1的槽函数进行连接
connect(btn3, SIGNAL(pressed()), ui->btn1, SLOT(hide()));
//参数1:信号的发射者,按钮3的指针
//参数2:要发射的信号,是在按钮所在类中拥有的信号函数,需要使用SIGNAL宏函数进行转换
//参数3:信号的接受者,ui界面上的按钮1指针
//参数4:处理信号的槽函数,是信号接收者所在类中拥有的槽函数
//注意:该链接方式是不友好的链接,原因是 即使宏函数中写错,编译器不报错,但是没有现象
//使用qt5版本的链接,将ui界面上的btn4按钮发射的clicked信号,与当前界面的自定义的槽函数连接
connect(ui->btn4, &QPushButton::clicked, this, &Widget::my_slot);
//将ui界面上的按钮5,发射的clicked信号,连接到全局函数中
connect(ui->btn5, &QPushButton::clicked, fun);
//将ui界面上的按钮5发射的clicked信号连接到lambda表达式中
connect(ui->btn5, &QPushButton::clicked, [&](){
// qDebug()<<"我是路人乙";
speecher->say("我是路人乙");
});
//将当前界面的my_signal信号连接到自定义的槽函数中
connect(this, &Widget::my_signal, [&](){
speecher->say("已经成功断开按钮3和按钮4的连接");
});
}
Widget::~Widget()
{
delete ui;
}
//自定义槽函数的实现
void Widget::my_slot()
{
static int num = 0;
if(num%2)
{
ui->btn2->setEnabled(false);
}else
{
ui->btn2->setEnabled(true);
}
num++;
}
//该函数就是按钮2的clicked信号对应的槽函数
void Widget::on_btn2_clicked()
{
static int num = 0;
if(num % 3 == 0)
{
ui->btn2->setStyleSheet("background-color:red;");
}else if(num % 3 == 1)
{
ui->btn2->setStyleSheet("background-color:yellow;");
}else
{
ui->btn2->setStyleSheet("background-color:green;");
}
num++;
}
//断开按钮对应的槽函数
void Widget::on_btn6_clicked()
{
disconnect(btn3, SIGNAL(pressed()), ui->btn1, SLOT(hide()));
disconnect(ui->btn4, &QPushButton::clicked, this, &Widget::my_slot);
//发射自定义的信号
emit my_signal();
}
4> ui界面
3> 选择Qt --》资源文件
4> 添加前缀
1> 一个信号可以连接到多个槽函数中,当信号被发射后,与之连接的所有槽函数都会被执行
2> 一个槽函数,可以连接多个信号函数,但凡其中一个信号被发射,那么该槽函数就会被执行
3> 一个信号函数,也可以连接到另一个信号函数上,表明当前一个信号发射时,后一个信号跟着被发射
4> 信号函数与槽函数参数个数总结
1、信号函数和槽函数进行链接时,一般要求信号函数和槽函数的参数保持一致
connect(信号发送者, SIGNAL(signalFun()),信号接收者, SLOT(slotFun())); //Ok
connect(信号发送者, SIGNAL(signalFun(int)),信号接收者, SLOT(slotFun(int))); //Ok
connect(信号发送者, SIGNAL(signalFun(int, char)),信号接收者, SLOT(slotFun(int, char))); //Ok
connect(信号发送者, SIGNAL(signalFun(char, int)),信号接收者, SLOT(slotFun(int, char))); //False
connect(信号发送者, SIGNAL(signalFun(int)),信号接收者, SLOT(slotFun(char))); //False
2、当信号函数的参数大于槽函数的参数时
connect(信号发送者, SIGNAL(signalFun(int, char)),信号接收者, SLOT(slotFun())); //Ok
connect(信号发送者, SIGNAL(signalFun(int, char)),信号接收者, SLOT(slotFun(int))); //Ok
connect(信号发送者, SIGNAL(signalFun(int, char)),信号接收者, SLOT(slotFun(char))); //False
3、当信号函数的参数小于槽函数的参数时
connect(信号发送者, SIGNAL(signalFun(int)),信号接收者, SLOT(slotFun(int, char))); //False
connect(信号发送者, SIGNAL(signalFun(int)),信号接收者, SLOT(slotFun(int, char=0))); //Ok
5> 案例
ui界面
头文件
#include
#include
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
signals:
void my_signal(); //自定义信号函数
void my_signal_1(QString msg); //自定义带参数的信号函数
public slots:
void btn1_slot1(); //声明函数
void btn1_slot2(); //声明函数
void my_signal_1_slot(QString m = "lallalallalaal");
private slots:
void on_btn3_clicked();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
源文件
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//1、一个信号可以连接多个槽函数,当信号发射时,所有与之连接的槽函数全部都执行
connect(ui->btn1, &QPushButton::clicked, this, &Widget::btn1_slot1);
connect(ui->btn1, &QPushButton::clicked, this, &Widget::btn1_slot2);
//2、一个槽函数可以连接多个信号
connect(ui->btn2, &QPushButton::clicked, this, &Widget::btn1_slot1);
//3、信号函数连接信号函数,表明发射第一个信号后,第二个信号跟着发射
connect(ui->btn2, &QPushButton::clicked, this, &Widget::my_signal);
connect(this, &Widget::my_signal, [](){
qDebug()<<"德玛西亚";
});
//4、连接my_signal_1信号到自定义的槽函数中
connect(this, SIGNAL(my_signal()), this, SLOT(my_signal_1_slot()));
}
Widget::~Widget()
{
delete ui;
}
//槽函数1的定义
void Widget::btn1_slot1()
{
qDebug()<<"Good good study";
}
//槽函数2的定义
void Widget::btn1_slot2()
{
qDebug()<<"day day up";
}
//按钮3发射的信号对应的槽函数
void Widget::on_btn3_clicked()
{
qDebug()<<"我现在想发射一个my_signal_1信号";
//emit my_signal_1("hello world");
emit my_signal();
}
//定义处理my_signal_1的槽函数
void Widget::my_signal_1_slot(QString m)
{
qDebug()<<"我是处理my_signal_1信号的槽函数:"<
1> 创建两个界面文件
在编辑器的项目处,邮件,add new
2> 创建一个新的界面
3> 两个界面的头文件
#ifndef WIDGET_H
#define WIDGET_H
#include
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
signals:
void jump(); //自定义跳转函数
private slots:
void on_jumpBtn_clicked();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
/***********************************第二个界面的头文件*******************/
#ifndef SECOND_H
#define SECOND_H
#include
namespace Ui {
class Second;
}
class Second : public QWidget
{
Q_OBJECT
public:
explicit Second(QWidget *parent = nullptr);
~Second();
public slots:
void jump_slot(); //定义有关处理跳转信号的槽函数
private:
Ui::Second *ui;
};
#endif // SECOND_H
4> 源文件
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//信号函数也可以连接信号函数
//connect(ui->jumpBtn, &QPushButton::clicked, this, &Widget::jump);
}
Widget::~Widget()
{
delete ui;
}
跳转按钮对应的槽 函数
void Widget::on_jumpBtn_clicked()
{
//发射跳转信号
emit jump();
//关闭当前 界面
this->hide();
}
/*************************************第二个文件的源文件****************/
#include "second.h"
#include "ui_second.h"
Second::Second(QWidget *parent) :
QWidget(parent),
ui(new Ui::Second)
{
ui->setupUi(this);
}
Second::~Second()
{
delete ui;
}
//处理跳转信号函数对应的槽函数
void Second::jump_slot()
{
this->show(); //将当前界面进行展示
}