C++基础 抽象类 类模板 STL库 QT环境
一、抽象类
1、纯虚函数
在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将虚函数改为纯虚函数。
语法:
virtual 返回值类型 函数名 (参数列表) = 0;
2. 抽象类
1) 概念
有纯虚函数所在的类,称为抽象类。
2) 特点
抽象类不能直接定义对象。
抽象类派生子类,子类必须实现所有的纯虚函数,否则子类也是一个抽象类。
抽象类可以定义指针。
示例代码:
#include <iostream>
#define PI 3.14159
using namespace std;
class Shap{ // 抽象类
public:
virtual double cal_s() = 0; // 纯虚函数
virtual double cal_c() = 0;
virtual void display() = 0;
};
class Square : public Shap{
private:
int longa;
int shorta;
public:
Square(int a,int b):longa(a),shorta(b){};
void display(){
cout<<"Square:";
}
double cal_s(){
return longa * shorta ;
}
double cal_c(){
return 2 * (longa + shorta);
}
};
class Circle : public Shap{
private:
int r;
public:
Circle(int r):r(r){};
void display(){
cout<<"Circle:";
}
double cal_s(){
return PI * r * r;
}
double cal_c(){
return 2 * PI * r ;
}
};
class Single : public Shap{
private:
int a;
int b;
int c;
public:
Single(int a,int b,int c):a(a),b(b),c(c){};
void display(){
cout<<"Single:";
}
double cal_s(){
return(b * a * c) ;
}
double cal_c(){
return a + b+ c;
}
};
void cal_s(Shap *p){
p->display();
cout<<"S = "<<p->cal_s()<<endl;
}
void cal_c(Shap *p){
p->display();
cout<<"C = "<<p->cal_c()<<endl;
}
int main(){
Square a(2,3);
Single b(1,3,3);
Circle c(3);
cal_c(&a);
cal_s(&a);
cal_c(&b);
cal_s(&b);
cal_c(&c);
cal_s(&c);
return 0;
}
输出:
二、类模板
1. 概念
实现一个通用类,类中所需的类型使用虚拟类型表示。一般通过类模板实现不同的数据结构,实现数据类型与数据结构的分离。同一个数据结构可以存储不同类型的数据。
2. 语法
Template <class/typename T>
Class 类名{ ... ... };
3. 使用类模板定义对象
必须显示指定类型。
类名 <指定类型> 对象名;
C++编译器会根据指定类型将类模板生成具体的类,然后再使用。
4. 类模板中的继承
派生普通类,必须指定具体类型派生
Class 派生类名: public 类模板<指定类型> { ... ... };
5. 类模板中的static成员
从类模板实例化的每个模板类都有自己的静态成员,同一个模板类的对象共享一个静态成员。
三、STL库
1. 理论
STL(Standard Template Library,标准模板库)是惠普实验室开发的一系列软件的统称。现然主要出现在C++中,但在被引入C++之前该技术就已经存在了很长的一段时间。
STL的从广义上讲分为三类:algorithm(算法)、container(容器)和iterator(迭代器),容器和算法通过迭代器可以进行无缝地连接。几乎所有的代码都采 用了模板类和模板函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。
在C++标准中,STL被组织为下面的13个头文 件:<algorithm>、<deque>、<functional>、<iterator>、<vector>、<list>、<map>、<memory>、<numeric>、<queue>、<set>、<stack> 和<utility>。
Class 派生类名: public 类模板<指定类型> { ... ... };
STL详细的说六大组件
- 容器(Container)
- 算法(Algorithm)
- 迭代器(Iterator)
- 仿函数(Function object)
- 适配器(Adaptor)
- 空间配制器(allocator)
使用STL的好处
(1)STL是C++的一部分,因此不用额外安装什么,它被内建在你的编译器之内。
(2)STL的一个重要特点是数据结构和算法的分离。尽管这是个简单的概念,但是这种分离确实使得STL变得非常通用。例如,在STL的vector容器中,可以放入元素、基础数据类型变量、元素的地址;STL的sort()函数可以用来操作vector,list等容器。
1) 程序员可以不用思考STL具体的实现过程,只要能够熟练使用STL就OK了。这样他们就可以把精力放在程序开发的别的方面。
2) STL具有高可重用性,高性能,高移植性,跨平台的优点。
高可重用性:STL中几乎所有的代码都采用了模板类和模版函数的方式实现,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。关于模板的知识,已经给大家介绍了。
高性能:如map可以高效地从十万条记录里面查找出指定的记录,因为map是采用红黑树的变体实现的。(红黑树是平横二叉树的一种)
高移植性:如在项目A上用STL编写的模块,可以直接移植到项目B上。
跨平台:如用windows的Visual Studio编写的代码可以在Mac OS的XCode上直接编译。
1) 程序员可以不用思考STL具体的实现过程,只要能够熟练使用STL就OK了。这样他们就可以把精力放在程序开发的别的方面。
2) 了解到STL的这些好处,我们知道STL无疑是最值得C++程序员骄傲的一部分。每一个C++程序员都应该好好学习STL。只有能够熟练使用STL的程序员,才是好的C++程序员。
3) 总之:招聘工作中,经常遇到C++程序员对STL不是非常了解。大多是有一个大致的映像,而对于在什么情况下应该使用哪个容器和算法都感到比较茫然。STL是C++程序员的一项不可或缺的基本技能,掌握它对提升C++编程大有裨益。
2. 容器
(1) 作用:
用来管理一组数据
(2) 分类:
1) 序列式容器(Sequence containers)
序列式容器是一种数据结构,它以线性序列的方式来存储某一特定类型的数据。这类容器并不会自动对存储的元素按照值的大小进行排序,而是保持元素的存储顺序与其在容器中的顺序一致。
2) 关联式容器(Associated containers)
关联式容器是一种数据结构,它支持高效的关键字查找和访问。关联式容器依照特定的排序准则自动为元素排序。
3. Vector
1) vector容器简介
vector是将元素置于一个动态数组中加以管理的容器,是通过类模板实现。
vector可以随机存取元素(支持索引值直接存取, 用[]操作符或at()方法)。
vector尾部添加或移除元素非常快速。但是在中部或头部插入元素或移除元素比较费时
包含头文件: #include <vector>
2) vector与普通数组的区别
普通数组是静态空间,vector可以动态拓展。
动态拓展:并不是在原空间之后接新的空间,而是找更大的内存空间,然后将
原数据拷贝到新空间,释放原空间。
3) 创建对象
vector<T> vecT; // 采用类模板实现,默认构造函数
vector(beg,end); // 构造函数将[beg, end)区间中的元素拷贝给本身。注意该区间是左闭右开的区间。
vector(n,elem); // 构造函数将n个elem拷贝给本身。
vector(const vector &vec); // 拷贝构造函数
4) 插入数据
insert(pos,elem); //在pos位置插入一个elem元素的拷贝
insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值
5) 遍历
at(int index);//返回下标index所在的元素
operator[];//返回下标index所在的元素
迭代器的方式遍历;//迭代器支持随机操作
6) 属性操作
size(); //获取容器大小
resize(); //设置容器大小
empty();//判断容器是否为空
7) 数据的存取
back(); // 返回最后一个元素
front(); // 返回第一个元素
push_back(); // 尾部追加
pop_back(); // 尾部删除
8) 删除操作
clear(); //移除容器的所有数据
erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置
erase(pos); //删除pos位置的数据,返回下一个数据的位置
示例代码:
#include <iostream>
#include <vector>
using namespace std;
int main(){
vector<int> v;
vector<int>::iterator it;
v.push_back(1); // 尾端插入元素
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
// 插入
it = v.begin();
v.insert(it + 2,9);
// 遍历
for(int i = 0;i<v.size();i++)
cout<<v[i]<<" ";
cout<<endl;
// 删除最后一个元素
v.pop_back();
// at遍历
for(int i = 0;i<v.size();i++)
cout<<v.at(i)<<" ";
cout<<endl;
// 删除(左闭右开)
v.erase(it,it + 2);
// 迭代器遍历
for(vector<int>::iterator it = v.begin();it != v.end();it++)
cout<<*it<<" ";
cout<<endl;
// 删除
it = v.begin();
v.erase(it + 1);
// 迭代器逆向遍历
cout<<"迭代器逆向遍历"<<endl;
for(vector<int>::reverse_iterator it = v.rbegin();it != v.rend();it++)
cout<<*it<<" ";
cout<<endl;
// 清空
v.clear();
if(v.empty()) cout<<"is empty"<<endl;
else cout<<"is not empty"<<endl;
return 0;
}
输出:
4. Deque
1) deque简介
deque是双端数组,也是通过类模板实现,deque头部和尾部添加或移除元素都非常快速。但是在中部安插元素或移除元素比较费时。
包含头文件:#include <deque>
2) 创建对象
deque<T> deqT;
deque(beg,end); //构造函数将[beg, end)区间中的元素拷贝给本身。注意该区间是左闭右开的区间。
deque(n,elem); //构造函数将n个elem拷贝给本身。
deque(const deque &deq); //拷贝构造函数。
3) 插入数据
deque.insert(pos,elem); // 在pos位置插入一个elem元素的拷贝,返回新数据的位置。
deque.insert(pos,n,elem); // 在pos位置插入n个elem数据,无返回值。
deque.insert(pos,beg,end); // 在pos位置插入[beg,end)区间的数据,无返回值。
4) 遍历
at(int index); //返回下标index所在的元素
operator[]; //返回下标index所在的元素
迭代器的方式遍历;//迭代器支持随机操作
5) 属性操作
size(); //获取容器大小
resize(); //设置容器大小
empty(); //判断容器是否为空
6) 数据的存取
back(); //返回最后一个元素
front(); //返回第一个元素
push_back(); //尾部追加
pop_back(); //尾部删除
push_front(); //头部插入
pop_front(); //头部删除
7) 删除
clear(); //移除容器的所有数据
erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置
erase(pos); //删除pos位置的数据,返回下一个数据的位置
示例代码:
#include <iostream>
#include <deque>
using namespace std;
// 输出
void display(deque<int> d){
deque<int>::iterator it;
cout<<"deque :";
for(it = d.begin();it != d.end();it++)
cout<<*it<<" ";
cout<<endl;
}
int main(){
deque<int>d;
deque<int>::iterator it;
d.push_back(1); // 尾部追加
d.push_back(2);
d.push_back(3);
d.push_front(-1); // 头部追加
d.push_front(-2);
d.push_front(-3);
display(d);
cout<<"第一个元素:"<<d.front()<<endl;
cout<<"最后一个元素:"<<d.back()<<endl;
cout<<"删除尾端元素"<<endl;
d.pop_back(); // 删除尾端元素
display(d);
cout<<"删除头端元素"<<endl;
d.pop_front(); // 删除头端元素
display(d);
cout<<"在第2个位置插入3个9"<<endl;
it = d.begin();
d.insert(it + 1,3,9); // 插入
display(d);
return 0;
}
输出:
5. List
1) list简介
list是一个双向链表容器,可高效地进行插入删除元素,通过类模板实现。
list不可以随机存取元素,所以不支持 at.(pos) 函数与[]操作符。it++(ok) it+5(err)
包含头文件:#include <list>
2) 创建对象
list<T> lstT; // 默认构造函数
list(beg,end); // 构造函数将[beg, end)区间中的元素拷贝给本身。注意该区间是左闭右开的区间
list(n,elem); // 构造函数将n个elem拷贝给本身
list(const list &lst); // 拷贝构造函数
3) 插入数据
list.insert(pos,elem); // 在pos位置插入一个elem元素的拷贝
list.insert(pos,n,elem); // 在pos位置插入n个elem数据,无返回值
list.insert(pos,beg,end); // 在pos位置插入[beg,end)区间的数据,无返回值
4) 遍历
通过迭代器遍历。迭代器只能进行自增操作,不支持it+n的操作。
5) 属性操作
size(); // 获取容器大小
resize(); // 设置容器大小
empty(); // 判断容器是否为空
6) 数据的存取
back(); // 获取尾部元素
front(); // 获取头部元素
push_back(); // 尾部追加
push_front(); // 头部追加
pop_back(); // 尾部删除
pop_front(); // 头部删除
7) 删除
list.clear(); //移除容器的所有数据
list.erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置
list.erase(pos); //删除pos位置的数据,返回下一个数据的位置
list.remove(elem); //删除容器中所有与elem值匹配的元素
8) 排序
List.sort() ; //默认按照升序排序
List.sort(com); // 将比较规则的入口地址传过来,可以实现升序或者降序,也可以对自定义的数据类型进行排序
示例代码:
#include <iostream>
#include <list>
using namespace std;
// 输出
void display(list<int> l){
list<int>::iterator it;
cout<<"list :";
for(it = l.begin();it != l.end();it++)
cout<<*it<<" ";
cout<<endl;
}
// 比较规则
bool Compare(int &x,int &y){
return x > y ? true : false;
}
int main(){
list<int> l;
list<int>::iterator it;
l.push_back(1);
l.push_back(3);
l.push_back(2);
l.push_front(-2);
l.push_front(-1);
l.push_front(-3);
display(l);
cout<<"第一个元素: "<<l.front()<<endl;
cout<<"最后一个元素: "<<l.back()<<endl;
cout<<"删除尾端元素"<<endl;
l.pop_back(); // 删除尾端元素
display(l);
cout<<"删除头端元素"<<endl;
l.pop_front(); // 删除头端元素
display(l);
cout<<"在第2个位置插入2个9"<<endl;
it = l.begin();
it++;
l.insert(it,2,9); // 插入元素
// it执行完上述代码后指向第4个元素
display(l);
cout<<"删除第3个元素"<<endl;
l.erase(--it); // 删除第3个元素
display(l);
cout<<"默认升序排序"<<endl;
l.sort();
display(l);
cout<<"降序排序"<<endl;
//l.sort(Compare); // 通过函数指针传入比较规则
l.sort();l.reverse(); // 先升序排序,然后再翻转
display(l);
return 0;
}