第31讲 多态
基本概念
多态(Polymorphism)是面向对象编程(OOP)的一个重要特性。在 C++ 中,多态是指用同一个函数名或操作符在不同的上下文中可以表现出不同的行为。简单来说,多态允许不同的对象对同一消息(函数调用)做出不同的响应。
多态的类型
(1)编译时多态(静态多态)
①函数重载(Function Overloading):在同一个作用域内,可以定义多个同名函数,但是它们的参数列表(参数个数、类型、顺序)不同。
示例如下:
cpp
#include <iostream>
using namespace std;
//静态多态
//函数名称重载
int add(int a, int b)
{
return a + b;
}
double add(double a, double b)
{
return a + b;
}
int main()
{
int x = 0, y = 10;
double pai = 3.14, e = 2.72;
int result0 = add(x, y);
double result1 = add(pai,e);
std::cout<< result0 <<endl;
std::cout << result1 <<endl;
system("pause");
return 0;
}
②运算符重载(Operator Overloading):C++ 允许重新定义运算符的操作,使其适用于用户自定义的数据类型。
示例如下:
cpp
#include <iostream>
using namespace std;
//静态多态
//重载运算符"+"实现复数相加
class Complex {
private:
double real;
double image;
public:
//含有初始化列表的有参构造
Complex(double r=0,double i=0):real(r),image(i) {}
Complex operator + (const Complex& other) {
Complex result;
result.image = this->image + other.image;
result.real = this->real + other.real;
return result;
}
void printInfo()
{
cout << "Complex = " << this->real << "+" << this->image << "j" << endl;
}
};
int main()
{
Complex c1(1, 2);
Complex c2(3, 4);
Complex c3 = (c1 + c2);
c3.printInfo();
system("pause");
return 0;
}
(2)运行时多态(动态多态)
虚函数(Virtual Function)和覆盖(Override):在基类中定义一个虚函数,然后在派生类中重新定义这个虚函数(函数签名相同),当通过基类指针或引用调用这个函数时,会根据对象的实际类型(是基类对象还是派生类对象)来决定调用基类的函数还是派生类的函数。
代码示例:
cpp
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw(void)
{
std::cout << "画了一个图形" << endl;
}
};
class Circle : public Shape {
public:
void draw(void) override
{
std::cout << "画了一个圆形" << endl;
}
};
int main()
{
Shape* shapePtr = new Circle; //多态机制让基类指针可以指向了派生类空间
shapePtr->draw();
delete shapePtr;
system("pause");
return 0;
}
作用
为什么使用多态?
灵活性 :允许我们编写可以处理不确定类型的对象的代码。
可扩展性 :我们可以添加新的派生类而不必修改使用基类引用或指针的代码。
接口与实现分离 :我们可以设计一个稳定的接口,而将具体的实现留给派生类去处理
第32讲 抽象类
基本概念
在 C++ 中,抽象类是一种特殊的类,它至少包含一个纯虚函数。纯虚函数是在基类中声明的虚函数,它在基类中没有定义具体的实现,只是通过在函数声明的结尾加上=0
来表示这是一个纯虚函数。例如:
cpp
class Shape {
public:
virtual double area() = 0; // 纯虚函数,计算形状的面积
};
上面的Shape类就是一个抽象类,因为它包含了纯虚函数area
。
特点
①不能实例化:抽象类不能直接用来创建对象。因为它包含了没有具体实现的纯虚函数,所以对象的完整定义是不明确的。例如,对于上面的Shape
类,如果试图这样创建对象:
②作为基类:抽象类主要是为其他类提供一个通用的接口,通常被用作基类。派生类必须实现抽象类中的纯虚函数,否则派生类也会成为抽象类。例如:
cpp
#include <iostream>
using namespace std;
class Shape {
public:
virtual double area()=0; //纯虚函数,计算不同图形面积需要不同的公式
};
class Circle : public Shape {
private:
double radius;
public:
Circle (double r): radius(r){}
double area() override
{
double area;
area = 3.14 * radius * radius;
cout << "area =" << area << endl;
return area;
}
};
int main()
{
Circle c1(5);
c1.area();
system("pause");
return 0;
}
可以输出结果:
但如果更改为:
用途
实现多态性:抽象类是实现多态性的重要手段。通过抽象类定义一个通用的接口,不同的派生类可以根据自身的特点来实现这个接口。例如,除了Circle
类,还可以定义Rectangle
类等不同的形状类,它们都继承自Shape
抽象类并实现area
函数。当有一个函数接受Shape*
类型的参数时,可以传入Circle
对象或者Rectangle
对象的指针,在函数内部根据对象的实际类型调用正确的area
函数实现,这就是多态性。
代码组织和设计:抽象类有助于组织代码结构,将具有共同特性的类进行抽象,使得代码更具逻辑性和可维护性。比如在一个图形绘制程序中,所有图形都有计算面积、绘制等操作,通过抽象类可以定义这些公共操作,而具体的图形类只需要关注自身特有的属性和操作的实现。
第33讲 接口
基本概念
在 C++ 中,虽然没有像其他编程语言(比如 Java 中的接口 Interface )一样直接定义接口的关键字,但可以通过抽象类和纯虚函数的方式来实现接口的概念。接口通常用于定义类应该实现的方法,但不提供具体实现。这样的实现方式允许多个类共享相同的接口,同时让每个类根据需要去实现这些接口。
实现步骤
一个类作为接口可以通过以下步骤来实现:
- 定义抽象类 :创建一个包含纯虚函数的抽象类,这些函数构成了接口的一部分。这些函数在抽象类中只有声明而没有具体的实现。
- 派生类实现接口 :派生类继承抽象类,并实现其中的纯虚函数,以具体实现接口定义的方法。
代码示例
以下是一个简单的示例来展示如何使用抽象类来模拟接口:
cpp
#include <iostream>
using namespace std;
class BasketBallMove {
public:
virtual void passTheBall() = 0;
};
class LiveMove {
public:
virtual void eat() = 0;
virtual void bite() = 0;
virtual void drink() = 0;
virtual void la() = 0;
};
class Human : public LiveMove, BasketBallMove {
public:
void eat() override {};
void bite() override {};
void drink() override {};
void la() override {};
void passTheBall() override {};
};
class Dog : public LiveMove {
public:
void eat() override {};
void bite() override {};
void drink() override {};
void la() override {};
};
int main()
{
Human h;
Dog g;
// LiveMove *l = new LiveMove;
system("pause");
return 0;
}
第34讲 C++汇总(友元、模板简介)
友元
概念
在 C++ 中,友元(Friend)机制是一种允许类外部的函数或者其他类访问该类的私有(private)和保护(protected)成员的机制。这在一定程度上破坏了类的封装性,但在某些特定情况下,它提供了很大的灵活性,比如在操作符重载等场景中非常有用。
例如:
①实现运算符重载,如重载输入输出运算符( << 和 >> )。
②允许两个不同类的对象之间进行密切的交互。
③当类设计需要一种安全控制的方式来允许非成员函数或类访问私有成员时。
类型
友元主要有三种类型:
1. 友元函数 **:**友元函数是一个在类定义中用关键字 friend
声明的非成员函数。它可以访问类的私有和保护成员,就好像它是类的成员函数一样。
代码示例:
cpp
#include <iostream>
using namespace std;
class Complex {
private:
double real;
double image;
public:
Complex(double r=0,double i=0):real(r),image(i){}
//声明友元函数addComplex
friend Complex addComplex(const Complex& c1, const Complex& c2);
void printInfo(void);
};
//实现友元函数addComplex的定义
Complex addComplex(const Complex& c1, const Complex& c2)
{
Complex result;
result.image = c1.image + c2.image;
result.real = c1.real + c2.real;
return result;
}
void Complex::printInfo(void)
{
if ((this->image)!=0)
{
cout << "Complex = " << this->real <<"+"<<this->image<<"j" << endl;
}
else {
cout << "Complex = " << this->real << endl;
}
}
int main()
{
Complex c0(1, -1);
Complex c1(1, 10);
addComplex(c0, c1).printInfo();
system("pause");
return 0;
}
在这个例子中,addComplex
函数是Complex
类的友元函数。它能够访问Complex
类的私有成员real
和image
,这样就可以方便地实现复数的加法运算。如果没有友元机制,就很难在类外部直接访问私有成员来进行这样的操作。
2. 友元类 :一个类可以被声明为另一个类的友元。这意味着友元类的所有成员函数都可以访问原始类的保护和私有成员。
代码示例:
cpp
#include <iostream>
using namespace std;
class Human {
private:
int age;
string name;
char sex;
public:
Human(int a=18,string n="Zeus",char s='m') :age(a),name(n),sex(s){}
friend class Teacher;
};
class Teacher {
private:
string major;
public:
int getAge(const Human& t0)
{
cout << t0.age << endl;
return t0.age;
}
};
int main()
{
Human h1;
Teacher t1;
t1.getAge(h1);
system("pause");
return 0;
}
在这个例子中,Teacher是Human的友元类。Teacher类中的getAge
函数可以访问Human类的私有成员age
。这在两个类之间存在紧密的关联和交互,并且需要互相访问对方的私有成员时非常有用。
模板
基本概念
在 C++ 中,模板( Template )是一种通用的编程工具,允许程序员编写 泛型代码 ,使得 类或函数能够适 用于多种不同的数据类型 而不需要重复编写相似的代码。
类型
C++ 提供了两种主要类型的模板:类模板和函数模板。
类模板(Class Templates):
类模板允许定义通用的类,其中某些类型可以作为参数。这样的类可以处理不同类型的数据,而不需要为每个数据类型编写单独的类。
代码示例:
cpp
#include <iostream>
using namespace std;
template <typename T>
class MyTemplate {
private:
T data;
public:
MyTemplate(T d):data(d){}
T getData(void)
{
return data;
}
};
int main()
{
// 使用类模板创建对象
MyTemplate <int> intObject(5);
MyTemplate <string> strObject("Hello");
// 调用类模板中的函数
cout << intObject.getData() << endl;
cout << strObject.getData() << endl;
system("pause");
return 0;
}
函数模板(Function Template)
函数模板允许编写通用的函数,可以处理多种不同类型的数据。
cpp
#include <iostream>
using namespace std;
template <typename T>
T add(T a, T b)
{
return a + b;
}
int main()
{
int result0 = add(1, 9);
cout << "计算结果为:" << result0 << endl;
double result1 = add(1.34, 3.14);
cout << "计算结果为:" << result1 << endl;
system("pause");
return 0;
}
模板提供了一种在编写代码时更具通用性的方法,能够处理不同类型的数据而无需为每种类型编写特定的函数或类。通过使用模板,可以提高代码的重用性和可维护性。
模板特化
模板特化( Template Specialization )是 C++ 中模板的一个概念,它允许针对特定的数据类型或特定的模板参数提供定制化的实现。模板特化允许您为模板提供一个特殊的实现,以覆盖或扩展默认的模板行为。 有两种类型的模板特化:完全特化(Full Specialization )和部分特化( Partial Specialization )。
完全特化
完全特化是对模板中的所有模板参数都进行特化的情况。在完全特化中,模板参数被指定为特定的类型,为特定的类型提供独特的实现。
代码示例:
cpp
#include <iostream>
#include <string>
using namespace std;
//先定义一个通用模板函数
template <typename T>
T maximum(T a, T b)
{
return (a > b) ? a : b;
}
//对模板函数进行完全特化,针对char*类型的比较
template <>
const char* maximum<const char*>(const char* a, const char* b)
{
return strcmp(a, b) < 0 ? a: b;
}
int main()
{
int max = maximum(100, 101);
cout << "更大的数是" << max << endl;
const char* charMax = maximum("Faker","Zeus");
cout << "更长的字符串是" << charMax << endl;
system("pause");
return 0;
}
部分特化
部分特化是指对模板中的部分参数进行特化,允许更具体地特化某些模板参数。这通常在模板类中被使用。 以下是一个示例,展示了对模板类的部分特化:
cpp
#include <iostream>
// 定义一个通用的模板类
template <typename T, typename U>
class MyTemplate
{
public:
void display()
{
std::cout << "Generic Display" << std::endl;
}
};
// 对模板类进行部分特化,针对特定的类型组合
template <typename T>
class MyTemplate<T, int>
{
public:
void display()
{
std::cout << "Specialized Display for T and int" << std::endl;
}
};
int main()
{
MyTemplate<float, double> obj1;
obj1.display(); // 输出 "Generic Display"
MyTemplate<int, int> obj2;
obj2.display(); // 输出 "Specialized Display for T and int"
system("pause");
return 0;
}
在这个示例中, MyTemplate 是一个模板类,然后我们对 MyTemplate 进行部分特化,当第二个模板参数是 int 类型时,提供了特殊的实现。因此,对于特定的类型组合,我们可以提供自定义的实现。
第35讲 记事本项目概述
项目需求
①支持文本创建,打开,保存,关闭的功能
②UI 样式美化
③添加打开快捷键,添加保存快捷
④底部显示行列号及文本字符编码
⑥Ctrl 加鼠标滚轮支持字体放大缩小
第36讲 QT工程默认各文件解析
创建步骤
①选择Application(Qt),之后选择Qt Widgets Application(Qt控件工程)
②定义编译系统,依旧选择qmake
③在类信息中对QMainWindow和QWidget做出二选一
MainWindows 还是 Widget
在 Qt 中,创建 "MainWindow" 与 "Widget" 项目的主要区别在于他们的用途和功能范围:
- MainWindow :这是一个包含完整菜单栏、工具栏和状态栏的主窗口应用程序框架。它适合于更复杂的应用程序,需要这些额外的用户界面元素来提供丰富的功能和交互。
- Widget :这通常是一个简单的窗口,没有内置的菜单栏、工具栏或状态栏。它适合于更简单或专用的应用程序,不需要复杂的用户界面组件。
简而言之,选择 "MainWindow" 或 "Widget" 取决于你的应用程序需要多少内置的用户界面元素和复杂性。 MainWindow 提供了更全面的框架,而 Widget 则更适合简单、专注的界面。
Generate form是否需要可拖动组件。
程序位数可32位也可以使用64位。
程序解析
Qt自动帮我们生成了这写代码:
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
QApplication
在 Qt 应用程序中, QApplication a(argc, argv); 这行代码的作用是创建一个 QApplication 类的
**实例。**这是几乎每个 Qt 应用程序必须做的第一步,因为它负责管理应用程序的许多核心功能。
下表总结了 QApplication 类在 Qt 框架中的主要功能和职责:
QApplication 是 Qt 应用程序的核心,它为应用程序提供了必要的环境和框架,确保 GUI 组件能够正常工作并响应用户的操作。简而言之, QApplication a(argc, argv); 用于初始化 Qt 应用程序的环境,设置事件循环,并准备应用程序处理GUI 事件。
Widget w
这一步实例化了一个Widget类的对象w,Widget这个base class的名称取决于创建步骤第三步。
跳转定义得:
cpp
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
};
Widget共有继承了QWidget类型,可以看到包括我们的按键等等等等可控组件都属于这个类。
return a.exec()
在 Qt 应用程序中, QApplication::exec() 函数是用来启动应用程序的事件循环的。当你调用这个函数时,它会开始处理和分发事件,如用户的点击、键盘输入等。这个函数会一直运行,直到事件循环结束,通常是因为调用了 QApplication::quit() 函数或者关闭了应用程序的主窗口。简而言之,
exec() 是 Qt 程序中的主循环,负责监听和响应事件,保持应用程序运行直到用户决定退出。
namespace Ui**{ class Widget; }**
在 Qt 框架中, namespace Ui { class Widget; } 是一种常见的用法,通常出现在使用 Qt Designer 设
计 GUI 时自动生成的代码中。这里的 Ui 是一个命名空间,而 class Widget 是一个前向声明,它声明了一个名为 Widget 的类。这种做法允许你在 .cpp 源文件中引用由 Qt Designer 创建的 UI 界面,而不需要在头文件中包含完整的UI 类定义。这种分离的方法有助于减少编译依赖性并保持代码的清晰和组织。在你的源文件中,你会创建一个 Ui::Widget 类型的对象来访问和操作 UI 组件。
QT_BEGIN_NAMESPACE
QT_BEGIN_NAMESPACE 是 Qt 框架中用于支持命名空间的宏定义。 Qt 使用这些宏来确保其库中的类和函数不会与其他库中的同名类和函数冲突。 QT_BEGIN_NAMESPACE 宏在定义 Qt 类和函数之前使用,用来指定接下来的代码位于Qt 的命名空间中。它通常与 QT_END_NAMESPACE 配对使用,后者标志着命名空间的结束。这种机制对于在大型项目中维护代码的清晰度和防止命名冲突非常重要。
Q_OBJECT
Q_OBJECT 宏是 Qt 框架中一个非常重要的宏,用于启用 Qt 对象的元对象系统。当你在 Qt 中定义一个类时,如果这个类继承自 QObject 或其子类,并且你想使用 Qt 的信号和槽机制、国际化、属性系统或其他Qt元对象系统提供的功能,就必须在类定义中包含 Q_OBJECT 宏。 这个宏允许 Qt 的元对象编译器( moc )识别并处理这个类,生成额外的代码,这些代码是实现信号和槽机制以及其他元对象功能所必需的。简单地说, Q_OBJECT 宏为 Qt 类提供了额外的元数据,使得类能够完全利用Qt 框架的功能。
**Widget::Widget(QWidget *parent) :**QWidget(parent),ui(new Ui::Widget)
代码 : QWidget(parent) 是初始化列表,用于调用基类 QWidget 的构造函数,并将 parent 传递给
它。 ui(new Ui::Widget) 是初始化类内部的 ui 成员变量,这是通过 new 关键字动态分配的。
Ui::Widget 是由 Qt Designer 工具生成的,用于处理用户界面。这种方式允许将用户界面的设计与后端逻辑代码分离,有助于提高代码的可维护性和可读性。