2023/9/13 -- C++/QT

作业:

1> 将之前定义的栈类和队列类都实现成模板类

栈:

#include <iostream>
#define MAX 40
using namespace std;template <typename T>
class Stack{
private:T *data;int top;
public:Stack();~Stack();Stack(const Stack &other);Stack &operator=(const Stack &other);//判空bool empty();//判满bool full();//获取栈的大小int size();//入栈void push();//出栈void pop();//获取栈顶元素T get_top();
};template <typename T>
Stack<T>::Stack():data(new T[MAX]),top(-1){cout<<"无参构造"<<endl;
}
template <typename T>
Stack<T>::~Stack(){delete []data;cout<<"析构函数"<<endl;
}
template <typename T>
Stack<T>::Stack(const Stack &other){top = other.top;data = new T[MAX];memcpy(data,other.data,sizeof(other.data[0])*(other.top+1));cout<<"拷贝构造"<<endl;
}
template <typename T>
Stack<T>&Stack<T>::operator=(const Stack &other){top = other.top;memcpy(data,other.data,sizeof(other.data[0])*(other.top+1));cout<<"拷贝赋值"<<endl;
}
//判空
template <typename T>
bool Stack<T>::empty(){return top == -1;
}
//判满
template <typename T>
bool Stack<T>::full(){return top == MAX-1;
}
//获取栈的大小
template <typename T>
int Stack<T>::size(){return top+1;
}
//入栈
template <typename T>
void Stack<T>::push(){if(full()){cout<<"栈满!入栈失败!"<<endl;return;}T value;cout<<"请输入入栈的元素:";cin>>value;top++;data[top] = value;cout<<value<<" 入栈成功"<<endl;
}
//出栈
template <typename T>
void Stack<T>::pop(){if(empty()){cout<<"栈空!出栈失败!"<<endl;return;}T temp = data[top--];cout<<temp<<" 出栈成功"<<endl;
}
//获取栈顶元素
template <typename T>
T Stack<T>::get_top(){return data[top];
}int main()
{Stack<int> s;s.push();s.push();s.push();cout<<"s.size = "<<s.size()<<endl;s.pop();cout<<"s.get_top = "<<s.get_top()<<endl;s.pop();s.pop();s.pop();return 0;
}

队列:

#include <iostream>
#define MAX 40
using namespace std;template<typename T>
class Queue{
private:T *data;int front;int back;
public:Queue();~Queue();Queue(const Queue &other);Queue &operator=(const Queue &other);//判空bool empty();//判满bool full();//获取队列大小int size();//入队void push();//出队void pop();
};
template <typename T>
Queue<T>::Queue():data(new T[MAX]),front(0),back(0){cout<<"无参构造"<<endl;
}
template <typename T>
Queue<T>::~Queue(){delete []data;cout<<"析构函数"<<endl;
}
template <typename T>
Queue<T>::Queue(const Queue &other){front = other.front;back = other.back;data = new T[MAX];memcpy(data,other.data,sizeof(other.data[0])*((other.back+MAX-other.front)%MAX));cout<<"拷贝构造"<<endl;
}
template <typename T>
Queue<T>&Queue<T>::operator=(const Queue &other){front = other.front;back = other.back;memcpy(data,other.data,sizeof(other.data[0])*((other.back+MAX-other.front)%MAX));cout<<"拷贝赋值"<<endl;
}
//判空
template <typename T>
bool Queue<T>::empty(){return front == back;
}
//判满
template <typename T>
bool Queue<T>::full(){return (back+1)%MAX == front;
}
//获取队列大小
template <typename T>
int Queue<T>::size(){return (back+MAX-front)%MAX;
}
//入队
template <typename T>
void Queue<T>::push(){if(full()){cout<<"队满!入队失败!"<<endl;return;}T value;cout<<"请输入要入队的元素:";cin>>value;data[back] = value;back = (back+1)%MAX;cout<<value<<" 入队成功"<<endl;
}
//出队
template <typename T>
void Queue<T>::pop(){if(empty()){cout<<"队空!出队失败!"<<endl;return;}T temp = data[front];front = (front+1)%MAX;cout<<temp<<" 出队成功"<<endl;
}int main()
{Queue<int> q;q.push();q.push();q.push();cout<<"q.size = "<<q.size()<<endl;q.pop();q.pop();q.pop();q.pop();cout<<"q.size = "<<q.size()<<endl;return 0;
}

 

2> 将继承和多态的课堂代码敲一遍

一、继承过程中的特殊成员函数

1.1 构造函数

1> 父子类中的构造函数不是同一个构造函数,父子类中拥有各自的构造函数

2> 需要在子类的构造函数初始化列表中,显性调用父类的有参构造完成对子类从父类中继承下来成员的初始化,否则系统会自动调用父类的无参构造来完成对其初始化,此时,如果父类中没有无参构造,则子类的构造函数会报错

3> 构造顺序:先构造父类再构造子类

1.2 析构函数

1> 父子类中会拥有各自的析构函数

2> 无需在子类的析构函数中显性调用父类的析构函数,当子类对象消亡时,系统会自动调用父类的析构函数,完成对子类从父类继承下来成员的内存回收工作,

3> 析构顺序:先析构子类再析构父类

1.3 拷贝构造

1> 父子类中拥有各自的拷贝构造函数

2> 需要在子类的拷贝构造函数初始化列表中,显性调用父类的拷贝构造函数完成对子类从父类中继承下来成员的初始化,如果没有显性调用父类的拷贝构造函数,系统会自动调用父类的无参构造来完成对其进行初始化工作,如果父类中没有无参构造,则子类的拷贝构造会报错

3> 如果父类中有指针成员指向堆区空间,则父类的拷贝构造需要使用深拷贝,如果子类有指针成员指向堆区空间,则子类需要使用深拷贝

1.4 拷贝赋值

1> 父子类中拥有给中的拷贝赋值函数

2> 需要在子类的拷贝赋值函数体内,显性调用父类的拷贝赋值函数,来完成对子类从父类中继承下来成员的赋值,如果没有显性调用父类的拷贝赋值函数,则系统啥也不干,子类从父类继承下来的成员的值保持不变。

3> 拷贝赋值也涉及深浅拷贝问题

二、多重继承

2.1 含义

所谓多重继承,就是可以由多个类共同派生出一个子类,该子类会继承所有父类的特征

2.2 继承格式

class  子类名:继承方式1 父类1,继承方式2 父类2,。。。,继承方式n 父类n
{//子类拓展成员
}

三、虚继承

3.1 菱形继承(钻石继承)问题

                                             A               -->    公共基类/   \B     C            -->    中间子类\   /D               -->    汇聚子类

在继承过程中,由一个公共基类,派生出多个中间子类,再由这若干个子类的一部分共同派生出一个汇聚子类,就会导致汇聚子类中保留多份由公共基类传下来的成员,使得子类内存冗余、并且访问起来也比较麻烦

3.2 虚继承

1> 为了解决以上的菱形继承问题,我们可以引入虚继承:就是在生成中间子类时,在继承方式前加关键字virtual,那么,再由这些中间子类生成汇聚子类时,汇聚子类中就只保留一份公共基类传下来的数据

2> 一般的继承,需要在子类的构造函数初始化列表中,显性调用直接父类的构造函数,完成对继承下来成员的初始化。但是,在虚继承中,由于汇聚子类中只有一份公共基类的成员,不能确定由哪个直接父类对公共基类继承下来成员进行初始化,索性不使用直接父类对其进行初始化工作,直接调用公共基类的构造函数来完成对从公共基类中继承下来成员的初始化工作,如果没有显性调用公共基类的有参构造,那么系统会自动调用公共基类的无参构造完成初始化工作,此时,如果公共基类中没有无参构造,则汇聚子类的构造函数会报错。

#include <iostream>using namespace std;class A
{
protected:int value_a;
public:A() {cout<<"A::无参构造"<<endl;}A(int a):value_a(a) {cout<<"A::有参构造"<<endl;}
};//虚继承格式:再生成中间子类时,继承方式前加virtual关键字
class B:virtual public A
{
protected:int value_b;
public:B() {cout<<"B::无参构造"<<endl;}B(int a, int b):A(a), value_b(b){cout<<"B::有参构造"<<endl;}
};class C:virtual public A
{
protected:int value_c;
public:C() {cout<<"C::无参构造"<<endl;}C(int a, int c):A(a), value_c(c){cout<<"C::有参构造"<<endl;}
};//由中间子类生成汇聚子类
class D:public B, public C
{
private:int value_d;
public:D() {}D(int a1, int a2, int b, int c, int d):A(a2),B(a1,b), C(a2,c), value_d(d) {cout<<"D::有参构造"<<endl;}void show(){cout<<"value_b = "<<value_b<<endl;cout<<"value_c = "<<value_c<<endl;cout<<"value_d = "<<value_d<<endl;
//        cout<<"value_a = "<<B::value_a<<endl;
//        cout<<"value_a = "<<C::value_a<<endl;cout<<"value_a = "<<value_a<<endl;}
};

四、多态

4.1 概念

所谓多态,就是一种形式的多种状态,多态是实现泛型编程的重要部分,能够实现“一条语句多用”

泛型编程:试图以不变的程序执行可变的功能

4.2 多态实现条件

1> 继承:没有继承就没有多态

2> 虚函数:实现函数重写,保证父子类中使用同一个函数

3> 父类指针或引用指向子类对象,调用子类中重写的父类的虚函数

4.3 虚函数

1> C++中可以将成员函数定义成虚函数,定义格式:在定义成员函数前加关键字virtual

2> 作用:以保证父类空间能够寻找到子类中重写的跟父类函数原型相同的函数

4.4 虚函数的底层实现

4.5 虚析构函数

1> 引入目的:正确引导delete关键字,在释放父类指针空间时,将子类空间一并释放

2> 定义格式:在析构函数前面加关键字virtual

3> 如果类中某个函数设置成虚函数,那么该类的子子孙孙类中的该函数都是虚函数

4> 在特殊的成员函数中,只有析构函数能设置成虚函数

5> 在定义类的时候,要养成将析构函数定义成虚析构函数,以便于内存的管理工作

#include <iostream>using namespace std;class Father
{
protected:string name;int age;public:Father() {}Father(string n, int a):name(n), age(a) {cout<<"Father::有参构造"<<endl;}virtual ~Father() {cout<<"Father::析构函数"<<endl;}
};class Son:public Father
{
private:string toy;public:Son(){}Son(string n, int a, string t):Father(n,a), toy(t) {cout<<"Son::有参构造"<<endl;}~Son(){cout<<"Son::析构函数"<<endl;}
};int main()
{//定义父类指针,指向堆区的子类对象Father *p = new Son("张三", 18, "car");//释放堆区空间delete p;return 0;
}

4.6 纯虚函数

1> 引入背景:对于一个类而言,某些函数没有实现的必要,或者实现这些函数没有意义。定义这些函数,纯粹为了让子类对其进行重写,以便于后期可以使用父类的指针或引用指向子类对象,去调用子类中重写的该函数,此时就可以将该函数定义成纯虚函数

2> 纯虚函数的定义格式:将虚函数的函数体去掉,用=0进行替换:virtual 函数类型 函数名(形参列表) = 0;

3> 包含纯虚函数的类称为抽象类,抽象类不能实例化对象

4> 纯虚函数必须由子类进行重写,如果子类中没有重写纯虚函数,那么在子类中该函数还依然是纯虚函数,子类也是抽象类

#include <iostream>using namespace std;class Shape
{
protected:double perimeter;double area;public:Shape():perimeter(0), area(0) {}//定义两个虚函数virtual double get_p() = 0;         //该函数就是纯虚函数virtual double get_a() = 0;};//定义圆形类
class Circle:public Shape
{
private:double radius;
public:Circle(double r = 0):radius(r){}//重写父类中提供的虚函数double get_a() override{area = 3.14*radius*radius;return area;}double get_p() override{perimeter = 2*radius*3.14;return perimeter;}
};//定义矩形类
class Rectangle:public Shape
{
private:double width;double height;
public:Rectangle(double w=0, double h=0):width(w), height(h){}//重写父类中提供的虚函数double get_a() override{area = width*height;return area;}double get_p() override{perimeter = 2*(width+height);return perimeter;}
};//定义全局函数输出给定图形的周长和面积
void show(Shape &s)
{cout<<"周长 = "<<s.get_p()<<"   面积 = "<<s.get_a()<<endl;//cout<<"周长 = "<<s.Shape::get_p()<<"   面积 = "<<s.Shape::get_a()<<endl;
}int main()
{//实例化一个圆形Circle c1(2);show(c1);Rectangle r1(3,4);show(r1);cout<<"*******************************"<<endl;//Shape s;                 //包含纯虚函数的类称为抽象类,抽象类不能实例化对象return 0;
}

五、泛型编程之模板(template)

模板是能够将数据类型进一步抽象,我们之前所接触的抽象,都是完成对数据的相关抽象

模板分为模板函数、模板类

5.1 模板函数

1> 程序员有时定义函数时,会因为函数参数类型不同,导致同一功能的函数需要定义多个,即使有函数重载,系统会自动匹配相应的函数,也会造成代码的冗余

2> C++提供函数模板机制:使用函数时,形参的类型也由实参进行传递,定义模板函数时,模板函数的参数,不仅能接收实参的值,也能接收实参的类型

3> 定义格式

template <typename 类型形参1,typename 类型形参2,。。。,typename 类型形参n>
函数返回值类型  函数名(形参列表){函数体内容}注意
template是定义模板的关键字
<>:中是类型的参数,用于接收实参传递的类型
typename:用于定义类型形参变量的,也可以使用class
函数体中,可以使用模板中的类型作为函数返回值、函数形参、局部变量的类型

4> 调用方式

1、隐式调用:调用时跟普通函数调用一样,系统会自动根据传进来的实参类型,推导出模板的类型参数

2、显式调用:在函数名后,实参括号前,加<>,传递类型形参的值,调用原则:尖找尖、圆找圆

3、一般模板函数要求显式调用

5> 注意事项

一个模板下面只能定义一个函数,如果要定义多个模板函数,需要定义多个模板

#include <iostream>using namespace std;//定义一个模板函数
template<typename T>
T sum(T m, T n)
{return m+n;
}//定义新的模板函数,不能多个模板函数共同使用一个模板
template<typename T>
T div(T m, T n)
{return m-n;
}int main()
{cout << sum<int>(3.3,5) << endl;           //8  显式调用模板函数cout << sum(3.2,5.3) << endl;        //8.5cout << sum(string("hello"),string("world")) << endl;return 0;
}

6> 模板函数的特化

当基础模板和特化模板同时出现时,如果是隐式调用,默认调用基础模板,如果是显式调用,则会调用特化模板

5.2 模板类

1> 引入背景:程序员有时定义类时,可能由于数据类型的不同,导致同样功能的类,需要定义多个,造成代码冗余问题

2> C++引入模板类,类中的一些类型可以是由调用时给定,由实参进行传递

3> 定义格式

template <typename 类型形参1,typename 类型形参2,。。。,typename 类型形参n>
class 类名
{//成员可以使用模板中的类型
};

4> 调用方式:只能显式调用,不能隐式调用

5> 在定义模板时,模板类外,但凡使用到类名,都需要重新再定义模板,并且使用类名时,需要给定类型

#include <iostream>using namespace std;template<typename T>
class Node
{
private:T data;              //数据域Node *next;           //指针域public:Node():next(NULL) {}           //无参构造Node(T d):data(d), next(NULL){}    //有参构造void show();};//在模板类外,但凡使用到模板类,都必须显性调用给定类型,需要重新定义模板
template <typename T>
void Node<T>::show()
{cout<<"data = "<<data<<endl;
}int main()
{Node<int> n1(520);        //模板类的使用必须显性调用n1.show();Node<double> n2(3.14);n2.show();return 0;
}

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

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

相关文章

TouchGFX之缓存位图

位图缓存是专用RAM缓冲区&#xff0c;应用可将位图保存&#xff08;或缓存&#xff09;在其中。 如果缓存了位图&#xff0c;在绘制位图时&#xff0c;TouchGFX将自动使用RAM缓存作为像素来源。位图缓存在许多情况下十分有用。 从RAM读取数据通常比从闪存读取要快&#xff08;特…

Linux下Minio分布式存储安装配置(图文详细)

文章目录 Linux下Minio分布式存储安装配置(图文详细)1 资源准备1.1 创建存储目录1.2 获取Minio Server资源1.3 获取Minio Client资源 2 Minio Server安装配置2.1 切换目录2.2 后台启动2.3 查看进程2.4 控制台测试 3 Minio Client安装配置3.1 切换目录3.2 移动mc脚本3.2 运行mc命…

Vue3后台管理系统Element-plus_侧边栏制作_无限递归

在home.view中添加代码 <template><div><div class"common-layout"><el-container><el-header class"common-header flex-float"><div class"flex"><img class"logo" src"../assets/logo…

【Redis】Redis实现分布式锁

【Redis】Redis常见面试题&#xff08;1&#xff09; 文章目录 【Redis】Redis常见面试题&#xff08;1&#xff09;1. 为什么要用分布式锁2. Redis如何实现分布式锁3. Redis接受多个请求模拟演示4. 使用Redis实现分布式锁会存在什么问题4.1 一个锁被长时间占用4.2 锁误删 【Re…

vue2+element-ui批量导入方法并判断上传的文件是否为xls或xlsx

业务需求: 代码结构: <el-dialogtitle"批量导入":close-on-click-modal"true"close"close()":visible"true"width"35%":center"true"><div class"el-dialog-div"><!-- 头部区域布局 -…

【基本数据结构 四】线性数据结构:队列

学习了栈后,再来看看第四种线性表结构,也就是队列,队列和栈一样也是一种受限的线性表结构,和栈后进先出的操作方式不同的是,队列是FIFO的结构,也就是先进先出的操作方式。 队列的定义 队列这个概念非常好理解。可以把它想象成排队买票,先来的先买,后来的人只能站末尾…

软考知识汇总--结构化开发方法

文章目录 1 结构化开发2 耦合3 内聚4 设计原则5 系统文档6 数据流图6.1 数据流图的基本图形元素 7 数据字典 1 结构化开发 结构化方法总的指导思想是自顶向下、逐层分解&#xff0c;它的基本原则是功能的分解与抽象。它是软件工程中最早出现的开发方法&#xff0c;特别适合于数…

「C++程序设计 (面向对象进阶)」学习笔记・二

0、引言 本专栏的系列文章是在学习 北京邮电大学 崔毅东 老师的《C程序设计 (面向对象进阶)》课程过程中整理的。欢迎前往专栏了解更多相关内容~ &#x1f600; 有关于现代 C 的基本介绍&#xff0c;请前往《现代C基本介绍》&#xff01; &#x1f514; 先决条件 本专栏的系列…

定时器+BOM

9.定时器BOM 1.定时器 **概念:**重复执行一个函数 1.1setInterval() setInterval(“代码/函数”,时间,参数),返回定时器的序列号,默认从1开始 clearInterval(序列号)清除定时 <button class"start">开启定时器</button><button class"close…

通过Power Platform自定义D365 CE 业务需求 - 3. 使用Microsoft Power应用程序

Microsoft Power Apps是一个用于开发应用程序的无代码、无代码平台。Power应用程序可以在Dataverse之上配置为数据库。尽管您可以连接Salesforce、OneDrive、Dropbox等多种云源,但Dataverse也可以用作内部数据库来构建应用程序,并通过连接器连接其他数据源进行集成。 Power应…

Java开发之Redis核心内容【面试篇 完结版】

文章目录 前言一、redis使用场景1. 知识分布2. 缓存穿透① 问题引入② 举例说明③ 解决方案④ 实战面试 3. 缓存击穿① 问题引入② 举例说明③ 解决方案④ 实战面试 4. 缓存雪崩① 问题引入② 举例说明③ 解决方案④ 实战面试 5. 缓存-双写一致性① 问题引入② 举例说明③ 解决…

内存管理机制

aCoral内存管理机制 aCoral内存管理机制在伙伴系统基础上&#xff0c;采用了位图法方式提高内存分配和回收速度的确定性&#xff0c;更能满足系统实时性的需求。 aCoral内存管理机制分为两级&#xff0c;上一级采用改进的伙伴系统&#xff0c;负责确定要分配的内存的大小&…

数据分析综述

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…

C#类与类库调用注意事项

类 创建一个类文件&#xff0c;myfunction.cs //静态类&#xff1a;直接引用、无需实例化 static public int jiafa(int V) //普通类&#xff1a;引用时需要实例化 public int jiafa(int V)using System; using System.Collections.Generic; using System.Diagnostics; using …

ChatGPT追祖寻宗:GPT-2论文要点解读

论文地址&#xff1a;Language Models are Unsupervised Multitask Learners 上篇&#xff1a;GPT-1论文要点解读 在上篇&#xff1a;GPT-1论文要点解读中我们介绍了GPT1论文中的相关要点内容&#xff0c;其实自GPT模型诞生以来&#xff0c;其核心模型架构基本没有太大的改变&a…

Vue中extend基本用法

1.Vue.extend(options) 参数: {Object} options用法&#xff1a; 使用基础Vue构造器&#xff0c;创建一个"子类"。参数是一个包含组件选项的对象。 data选项是特例&#xff0c;需要注意&#xff0c;在Vue.extend()中它必须是函数。 <html><head><tit…

读高性能MySQL(第4版)笔记08_创建高性能索引(上)

1. 索引 1.1. 键&#xff08;key&#xff09; 1.2. 存储引擎用于快速找到记录的一种数据结构 1.3. 当表中的数据量越来越大时&#xff0c;索引对性能的影响愈发重要 1.4. 在数据量较小且负载较低时&#xff0c;缺少合适的索引对性能的影响可能还不明显 1.5. 索引优化是对查…

C#__线程池的简单介绍和使用

/*线程池原理&#xff1a;&#xff08;有备无患的默认备用后台线程&#xff09;特点&#xff1a;线程提前建好在线程池;只能用于运行时间较短的线程。*/class Program{static void Main(string[] args){for (int i 0; i < 10; i){ThreadPool.QueueUserWorkItem(Download); …

【Redis】Redis作为缓存

【Redis】Redis常见面试题&#xff08;2&#xff09; 文章目录 【Redis】Redis常见面试题&#xff08;2&#xff09;1. 缓存2. Redis作为缓存2.1 缓存雪崩2.2 缓存穿透2.3 缓存击穿2.4 缓存雪崩、缓存穿透、缓存击穿的区别2.5 缓存预热2.6 如何保证缓存和MySQL双写一致 【Redis…

Java 设置免登录请求接口被拦截问题

1、在设置免登录时&#xff0c;前端将请求的路由添加到白名单后&#xff0c;请求接口还是被拦截到了&#xff0c;将请求接口也设置后还是会被拦截跳转到登录页面 通过JAVA 注解 Anonymous 进行设置匿名访问就可以了