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.hqwc.cn/news/108321.html

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

相关文章

uniapp 触底加载

方式一 onReachBottomDistance 缺点&#xff1a;需要整个页面滑动&#xff0c;局部滑动触发不了 { // pages.json // 路由下增加 onReachBottomDistance "path": "detailed/detailed","style": {"navigationBarTitleText": "收…

【实战详解】如何快速搭建接口自动化测试框架?Python + Requests

摘要&#xff1a; 本文主要介绍如何使用Python语言和Requests库进行接口自动化测试&#xff0c;并提供详细的代码示例和操作步骤。希望能对读者有所启发和帮助。 前言 随着移动互联网的快速发展&#xff0c;越来越多的应用程序采用Web API&#xff08;也称为RESTful API&…

WebDAV之π-Disk派盘 + BubbleUPnP

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

算法:贪心---跳一跳

1、题目&#xff1a; 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 2…

【ES实战】ES中关于segment的小结

文章目录 ES中关于segment的小结ES中segment相关的原理在Lucene中的产生segment的过程。&#xff08;Lucene commit过程&#xff09;ES为了实现近实时可查询做了哪些缩短数据可被搜索的等待时长增加数据的可靠性优化segment的数量 段合并自动合并强制合并 相关配置translog合并…

计算机二级知识点整理

翻到了之前准备计算机二级的笔记&#xff0c;现在给大家分享出来。 一、基本知识&#xff1a; 计算机把完成一条指令所花费的时间称为一个指令周期结构化程序设计强调的是程序的易读性boolean类型不能转为其他基本类型数据表达式是由运算符和运算对象构成的&#xff0c;#不是…

分库分表---理论

目录 一、垂直切分 1、垂直分库 2、垂直分表 3、垂直切分优缺点 二、水平切分 1、水平分库 2、水平分表 3、水平切分优缺点 三、数据分片规则 1、Hash取模分表 2、数值Range分表 3、一致性Hash算法 四、分库分表带来的问题 1、分布式事务问题 2、跨节点关联查询…

全局滚动条样式修改,elementUI table底部滚动条遮挡

/* 整个滚动条 */ ::-webkit-scrollbar {width: 15px !important;height: 15px !important; } /* 滚动条上的滚动滑块 */ ::-webkit-scrollbar-thumb {border-radius: 32px; } /* 滚动条轨道 */ ::-webkit-scrollbar-track {border-radius: 32px; }// 如果想作用组件 可以 .xxx…

Plotly进行数据可视化初体验

Plotly库是一个交互式开源库。这对于数据可视化和简单轻松地理解数据是非常有用的工具。plotly图形对象是plotly的高级接口&#xff0c;易于使用。它可以绘制各种类型的图形和图表&#xff0c;如散点图&#xff0c;折线图&#xff0c;条形图&#xff0c;箱形图&#xff0c;直方…

DHT11 温湿度传感器

目录 1.DHT11 温湿度传感器概述 2.检测DHT11温湿度传感器模块是否存在 3.通过编写代码读取温湿度数据​编辑 4.将读取到的温湿度数据通过串口上传 1.DHT11 温湿度传感器概述 DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器&#xff0c;应用领域&am…

后端字典的优雅设计

背景 今天讲到的是数据字典的设计。为什么要讲到这个呢&#xff0c;因为我下午在做开发的时候遇到了一个问题。我先扔出来某个表的字段的定义吧&#xff1a; business_type int default 0 comment 0&#xff1a;收款计划&#xff1b;1&#xff1a;付款计划而且我还有一个字典…

导数公式及求导法则

目录 基本初等函数的导数公式 求导法则 有理运算法则 复合函数求导法 隐函数求导法 反函数求导法 参数方程求导法 对数求导法 基本初等函数的导数公式 基本初等函数的导数公式包括&#xff1a; C0(x^n)nx^(n-1)(a^x)a^x*lna(e^x)e^x(loga(x))1/(xlna)(lnx)1/x(sinx)cos…