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.rhkb.cn/news/131917.html

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

相关文章

时序数据库 TimescaleDB 安装与使用

TimescaleDB 是一个时间序列数据库&#xff0c;建立在 PostgreSQL 之上。然而&#xff0c;不仅如此&#xff0c;它还是时间序列的关系数据库。使用 TimescaleDB 的开发人员将受益于专门构建的时间序列数据库以及经典的关系数据库 (PostgreSQL)&#xff0c;所有这些都具有完整的…

数据库与身份认证

1. 数据库的基本概念 1.1 什么是数据库 数据库&#xff08;database&#xff09;是用来组织、存储和管理数据的仓库。 当今世界是一个充满着数据的互联网世界&#xff0c;充斥着大量的数据。数据的来源有很多&#xff0c;比如出行记录、消费记录、浏览的网页、发送的消息…

防火墙(Firewall)

目录 一、概述 二、iptables 三、iptable的用法 一、概述 防火墙的作用 用于保护内网安全的一种设备 依据规则进行防护 用户定义规则 允许或拒绝外部用户访问 防火墙分类 逻辑上划分&#xff0c;防火墙可以大体分为主机防火墙和网络防火墙主机防火墙&#xff1a;针对…

Redis缓存设计与性能优化

多级缓存架构 缓存设计 缓存穿透 缓存穿透是指查询一个根本不存在的数据&#xff0c; 缓存层和存储层都不会命中&#xff0c; 通常出于容错的考虑&#xff0c; 如果从存储层查不到数据则不写入缓存层。缓存穿透将导致不存在的数据每次请求都要到存储层去查询&#xff0c; 失去…

WebDAV之π-Disk派盘 + BubbleUPnP

BubbleUPnP是一款功能强大的Android播放器,支持UPnP/DLNA多屏互动。它可以将手机内容投屏到电视大屏上,与家人和朋友一起共享。此外,BubbleUPnP还提供了丰富的音乐和影视资源,您可以在线搜索并播放喜欢的内容。 以下是BubbleUPnP的一些主要特点: 1. 支持Chromecast和转码…

在PHP8中向数组添加元素-PHP8知识详解

在php8中向数组添加元素有多种方法&#xff0c;在这里主要讲解几个常用的方法&#xff1a;使用方括号[]添加元素、使用array_unshift()函数&#xff0c;向数组的头部添加元素、使用array_push()函数&#xff0c;向数组的尾部添加元素、使用array_splice()函数添加元素。 1、使用…

【新版】系统架构设计师 - 软件架构设计<SOA与微服务>

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 架构 - 软件架构设计&#xff1c;SOA与微服务&#xff1e; 考点摘要 面向服务SOA&#xff08;★★★★&#xff09;微服务&#xff08;★★★★&#xff09; 基于/面向服务的&#xff08;SOA&#xff09; 在SO…

【深度学习-注意力机制attention 在seq2seq中应用】

注意力机制 为什么需要注意力机制attention机制的架构总体设计一、attention本身实现评分函数 attention在网络模型的应用-Bahdanau 注意力加性注意力代码实现 为什么需要注意力机制 这是一个普通的seq2seq结构&#xff0c;用以实现机器对话&#xff0c;Encoder需要把一个输入的…

Linux下的系统编程——信号(十一)

前言&#xff1a; 信号在我们的生活中随处可见&#xff0c; 如&#xff1a;古代战争中摔杯为号&#xff1b;现代战争中的信号弹&#xff1b;体育比赛中使用的信号枪...... 他们都有共性&#xff0c;信号是信息的载体&#xff0c;Linux/UNIX 环境下&#xff0c;古老、经典的通信…

SpringBoot系列(12):SpringBoot集成log4j2日志配置

最近项目上有使用到log4j2日志模板配置&#xff0c;本文简单总结一下之前的学习笔记&#xff0c;如有纰漏之处&#xff0c;请批评指正。 1. log4j2日志依赖 使用log4j2日志模板时&#xff0c;需要引入相关依赖&#xff0c;下边的两种依赖方式均可。 1.1 使用sl4j依赖时 <…

Mapbox加载arcgis的底图

成果图 这种底图基本上都是按照raster来加载的&#xff0c;主要就是知道地址了&#xff0c;拼参数 具体参数请参考官网 https://developers.arcgis.com/rest/services-reference/enterprise/export-map.htm 源码 我的服务列表是这样的 http://XXXX:XXXX/arcgis/rest/services/…

电子游戏冷知识

电子游戏一直在试图用技术还原一个真实或虚幻的世界&#xff0c;并在其中演绎和倾诉人类种种的情感和欲望。 对信息技术发展的贡献 游戏推动了芯片、网络、VR/AR等领域的技术进步和创新。根据中科院的研究报告&#xff0c;游戏技术对芯片产业的科技进步贡献率是14.9%&#xff…

Android13 大屏设备底部显示TaskBar并NavagatonBar居右

Android 13大屏设备时底下显示任务栏以及虚拟按键靠右的问题&#xff0c; 当前需求是去掉底部任务栏的显示&#xff0c;并把虚拟按键导航栏居中显示。 修改前的效果&#xff1a; 修改后的效果&#xff1a; 通过查看源码逻辑&#xff0c;可以发现只需把isTablet相关的逻辑和…

Vue3路由

文章目录 Vue3路由1. 载入vue-router 库2. 实例2.1 Vue.js vue-router 实现单页应用2.2 router-link创建链接2.3 router-view显示与url对应组件2.4 <router-link> 相关属性 Vue3路由 1. 载入vue-router 库 Vue.js 路由需要载入vue-router 库 安装直接下载地址&#xf…

【陕西理工大学-数学软件实训】数学实验报告(8)(数值微积分与方程数值求解)

目录 一、实验目的 二、实验要求 三、实验内容与结果 四、实验心得 一、实验目的 1. 掌握求数值导数和数值积分的方法。 2. 掌握代数方程数值求解的方法。 3. 掌握常微分方程数值求解的方法。 二、实验要求 1. 根据实验内容&#xff0c;编写相应的MATLAB程序&#xff0c…

阿里云服务器部署安装hadoop与elasticsearch踩坑笔记

2023-09-12 14:00——2023.09.13 20:06 目录 00、软件版本 01、阿里云服务器部署hadoop 1.1、修改四个配置文件 1.1.1、core-site.xml 1.1.2、hdfs-site.xml 1.1.3、mapred-site.xml 1.1.4、yarn-site.xml 1.2、修改系统/etc/hosts文件与系统变量 1.2.1、修改主机名解…

【案例教学】华为云API对话机器人的魅力—体验AI垃圾分类机器人

云服务、API、SDK&#xff0c;调试&#xff0c;查看&#xff0c;我都行 阅读短文您可以学习到&#xff1a;人工智能AI自言语言的情感分析、文本分词、文本翻译 1 IntelliJ IDEA 之API插件介绍 API插件支持 VS Code IDE、IntelliJ IDEA等平台、以及华为云自研 CodeArts IDE&a…

16G FC SFP+ SW光模块应用解析

随着云计算、大数据和物联网等技术的迅猛发展&#xff0c;数据传输速率不断提高。传统的铜缆传输面临带宽瓶颈和信号衰减等问题&#xff0c;而光纤传输凭借其高带宽、低损耗等优势成为了现代通信的主要选择。易天光通信的16G SFP SW 多模光模块作为高性能光纤传输设备&#xff…

数据分析三剑客之Pandas

1.引入 前面一篇文章我们介绍了numpy&#xff0c;但numpy的特长并不是在于数据处理&#xff0c;而是在它能非常方便地实现科学计算&#xff0c;所以我们日常对数据进行处理时用的numpy情况并不是很多&#xff0c;我们需要处理的数据一般都是带有列标签和index索引的&#xff0…

【PHY】3GPP UE能力类别的变化

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…