最近在做算法移植的时候,发现自己对c++的知识忘得很多,于是就计划重新捡起来。
纸上得来终觉浅,绝知此事要躬行
一直都是我学习和工作的原则,我比较喜欢动手。
于是我把c++的基础知识一行行代码敲了一遍!
1.1 模板的概念
模板就是建立通用的摸具,提高复用性。
模板的特点:
- 模板不可以直接使用,它只是一个框架
- 模板的通用不是万能的
- c++的另一种编程思想称为泛型编程,主要利用的技术就是模板
- c++提供2种模板机制:函数模板和类模板
函数模板的作用:
建立一个统一的函数,其函数返回值类型和形参类型可以不具体,用一个虚拟的类型来代表。
语法:
template函数声明或者定义 **解释:** * template ---声明创建模板 * typename --- 表面一种数据类型,可以用class代替 * T --- 通用的数据类型,名字可以随便取,通常为大写字母 template //或者template void mySwap(T &a,T &b) { T temp; temp = a; a = b; b = temp; } int main(int argc, char** argv) { int a = 10; int b = 20; //2种方式使用函数模板 //1.自动推导类型 mySwap(a,b); //2.显示指定类型 mySwap (a,b); cout<<"a="< 2种方式使用函数模板
1.2.2 函数模板的注意事项
- 1.自动推导类型
mySwap(a,b);- 2.显示指定类型
mySwap(a,b);注意事项:
- 自动类型推导,必须推导出一致的数据类型,才可以使用
template//或者template void mySwap(T &a,T &b) { //省略 } void test1() { int a = 10; char b = 'c'; //mySwap(a,b);报错,a和b类型不一致 }
- 模板必须要确定出T的数据类型,才可以使用
template1.2.3 普通函数和函数模板的区别void func()//函数模板 { } void test2() { //func();//报错,没指定类型 func ();//正确 }
- 普通函数调用时可以发生自动类型转换(隐式类型转换)
- 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
- 如果利用显示指定类型方式,可以发生显示转换
//普通函数 int add(int a,int b) { cout< T myAdd(T a,T b) { cout<(a,b); return 0; }1.2.4 普通函数和函数模板的调用规则
- 1.如果函数模板和普通函数都可以实现,优先调用普通函数
- 2.可以通过空模板参数列表来强制调用函数模板
- 3.函数模板也可以发生重载
- 4.如果函数模板可以产生更好的匹配,优先调用函数模板。
//普通函数 void myPrint(int a,int b) { cout<<"普通函数 调用"<1.2.5 模板的局限性void myPrint(T a,T b) { cout<<"函数模板 调用"< void myPrint(T a,T b,T c) { cout<<"函数模板 重载调用"< (10,20);//强制调用模板函数 myPrint(10,20,30);//调用重载的函数模板 myPrint('a','b');//函数模板更匹配 return 0; } 局限性:
模板通用性不是万能的class Person { public: Person(string n,int a):name(n),age(a){} string name; int age; } ; templatebool myCompare(T &a,T &b) { if(a == b)//无法比较Person类 return true; return false; } //利用具体化Person的版本实现代码,具体化优先调用 template<> bool myCompare(Person &a,Person &b) { if(a.age == b.age && a.name == b.name) return true; return false; } void test1() { Person p1("Tom",10); Person p2("Tom",10); myCompare(p1,p2); } 1.3 类模板 1.3.1 类模板语法
- 利用具体化的模板,可以解决自定义类型的通用化
- 学习模板并不是为了写模板,而是在STL能够运用系统提供的模板
类模板的作用:
- 建立一个通用类,类中的成员数据类型可以不具体指定,用一个虚拟的类型来代表
语法:
template类 templateclass Person { public: Person(T n,P a):name(n),age(a){} T name; P age; }; int main() { Person p1("Tom",10); cout< 1.3.2 类模板与函数模板的区别
- 1.类模板没有自动类型推导的使用方式,必须显示指定类型
- 2.类模板在模板参数列表中可以有默认参数
template1.3.3 类模板中 成员函数的创建时机//默认整型 class Person { public: Person(T n,P a):name(n),age(a){} T name; P age; }; int main(int argc, char** argv) { Person p1("tom",10); Person p2("tom",10);//<>里面的int可以省略 return 0; } 1.3.4 类模板对象做函数参数
- 普通类中的成员函数一开始就可以创建
- 类模板中的成员函数在调用时才创建
- 1.指定传入的类型 — 直接显示对象的数据类型
- 2.参数模板化 — 将对象中的参数变为模板进行传递
- 3.整个类模板化 --将这个对象类型 模板化进行传递
template1.3.5 类模板与继承class Person { public: Person(T n,P a):m_age(a),m_name(n){} void showPerson() { cout< m_name<<" "< m_age< &p) { p.showPerson(); } void test1() { Person p1("tom",10); printPerson1(p1); } //2.参数模板化 template void printPerson2(Person &p) { p.showPerson(); } void test2() { Person p1("tom2",10); printPerson2(p1); } //3.整个类模板化 template void printPerson3(T &p) { p.showPerson(); } void test3() { Person p1("tom3",10); printPerson3(p1); }
- 当子继承的父类时一个类模板时,子类在声明的时候,要指定父类中T的类型
- 如果不指定,编译器无法为子类分配内存
- 如果想灵活指定父类中T的类型,子类也需要变成类模板
template1.3.6 类模板成员函数类外实现class base { public: T a; }; class Son : public base //指定类型 { }; template class Son2 : public base //灵活指定类型 { T1 obj; }; 类模板中的成员函数类外实现时,需要加上模板参数列表
template1.3.7 类模板分文件编写class Person { public: Person(T1 n,T2 a); void show(); T1 m_name; T2 m_age; }; template Person ::Person(T1,T2) { } template void Person ::show() { } 问题:
- 类模板成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:- 解决方式1:直接包含cpp文件
- 解决方式2:将声明和实现写到同一个文件红,然后改后缀名为hpp,hpp是约定的名称,非强制!
Person.h#include#include using namespace std; template class Person { public: Person(); Person(T1 name,T2 age); ~Person(); void showPerson(); T1 name; T2 age; }; Person.cpp
#include "Person.h" templatePerson ::Person(T1 name,T2 age) { this->name = name; this->age = age; } template Person ::~Person() { } template void Person ::showPerson() { cout< main.cpp
- 解决方式1:直接包含cpp文件 很少这么做
//#include "Person.h" //类模板的成员函数编译时不会创建,因此会找不到引用 #include "Person.cpp" / void myPrint(int val) { cout<v; //2.插入数据 v.push_back(10); v.push_back(20); v.push_back(30); //通过迭代器访问 vector ::iterator itBegin = v.begin();//起始迭代器,指向容器第一个元素 vector ::iterator itEnd = v.end();//结束迭代器,指向容器最后一个元素的下一个位置 //第一种遍历方式 while(itBegin != itEnd) { cout<<*itBegin++< ::iterator it = v.begin();it!=v.end();it++) { cout<<*it< 2.5.2 vector存放自定义数据类型 class Person { public: Person(string n,int a):name(n),age(a){} string name; int age; }; void myPrint(Person p) { cout<name<<" "< age< v; Person p1("a",10); Person p2("b",20); Person p3("c",30); Person p4("d",40); v.push_back(p1); v.push_back(p2); v.push_back(p3); v.push_back(p4); //方式1 遍历 for(vector ::iterator it = v.begin();it!=v.end();it++) { cout<<(*it).name<<" "<<(*it).age< name<<" "< age< v; Person p1("a",10); Person p2("b",20); Person p3("c",30); Person p4("d",40); v.push_back(&p1); v.push_back(&p2); v.push_back(&p3); v.push_back(&p4); //方式1 遍历 for(vector ::iterator it = v.begin();it!=v.end();it++) { cout<<(*it)->name<<" "<<(*it)->age< 2.5.3 vector嵌套容器 //容器嵌套容器 void test() { //1.创建大容器 vector> v; //2.创建小容器 vector v1; vector v2; //小容器赋值 for(int i=0;i<2;i++) { v1.push_back(i+1); v1.push_back(i+1); v1.push_back(i+1); v2.push_back(i+2); v2.push_back(i+2); v2.push_back(i+2); } //大容器赋值 v.push_back(v1); v.push_back(v2); //遍历数据 for(vector >::iterator it = v.begin();it != v.end();it++) { for(vector ::iterator vit = it->begin();vit!=it->end();vit++) { cout<<*vit<<" "; } cout< 3.1 string容器 3.1.1 string基本概念 本质:
- string是c++的字符串,本质上是一个类
string和char*的区别
- char *是一个指针
- string是一个类,内部封装了char指针,管理字符串,是一个char类型的容器
特点:
3.1.2 string构造函数
string类内封装了很多成员方法,如find,copy,delete等。
string管理char*所分配的内存,不用担心复制越界和取值越界问题,都类内部进行负责
- string():创建一个空的字符串,例如string s;
- string(const char *s):使用字符串s初始化
- sting(const string& str):使用string对象初始化
- string(int n,char c):使用n个字符c初始化
省略string的一些内容···
3.2 vector容器 3.2.1 vector基本概念
- vector数据结构和数组非常相似,也称为单端数组
- vector可以动态扩展空间,数组是空间是不可变的
- vector容器的迭代器支持随机访问
动态扩展:
3.2.2 vector构造函数
- 并不是原空间之后申请新的空间,而是找到更大的内存空间,然后将原有数据拷贝到新空间,释放原空间。
函数原型:
3.3 deque容器 3.4 stack容器-栈 3.5 queue-队列 3.6 list容器-双向循环链表 3.7 set/multiset容器-二叉树 3.8 map/multimap容器
- vector v:默认构造
- vector(v.begin(),v.end()):将v.begin()到v.end()中的元素拷贝给自己
- vector(n,elem):把n个elem拷贝给自己
- vector(const vector &vec):拷贝构造函数
4.STL-函数对象 4.1 函数对象 4.1.1函数对象的概念概念
- 重载函数调用 *** 作符的类,其对象常常称为函数对象
- 函数对象使用重载的()时,行为类似函数调用,也成为仿函数
本质:
4.1.2 函数对象的使用
函数对象(仿函数)是一个类,不是函数特点:
- 函数对象在使用时,可以像普通函数那样调用,可以有参数和返回值
- 函数对象可以有自己的状态
- 函数对象可以作为参数传递
class MyAdd { public: int operator()(int a,int b) { return a+b; } } ; //1. 函数对象在使用时,可以像普通函数那样调用,可以有参数和返回值 void test1() { MyAdd add; cout< 4.2 谓词 4.2.1 谓词的概念概念:
4.2.2 一元谓词
- 返回bool的类型的仿函数称为 谓词
- 如果operator()接受1个参数,称为 一元谓词
- 如果operator()接受2个参数,称为 二元谓词
//1.一元谓词 class GreterFive { public: bool operator()(int v) { return v > 5; } }; void test1() { vectorv; for (int i = 0; i < 10; i++) v.push_back(i); //查找大于5的值,这里 new GreterFive()是一个匿名对象 vector ::iterator it = find_if(v.begin(), v.end(), GreterFive()); if (it == v.end()) { cout << "没有找到大于5的值" << endl; } else { cout<<"找到大于5的值:"<<*it< 4.2.3 二元谓词 //二元谓词 class Mycompare { public: bool operator()(int v1,int v2) { return v1>v2; } }; void test() { vectorv; v.push_back(10); v.push_back(30); v.push_back(20); v.push_back(50); Mycompare m; sort(v.begin(),v.end(),m); //sort(v.begin(),v.end(),Mycompare());// Mycompare()是匿名对象 for(vector ::iterator it = v.begin();it!=v.end();it++) { cout<<*it<<" "; } cout< 4.3 内建函数对象 4.3.1 概念 概念:
4.3.2 算术仿函数
- STL内建了一些函数对象
分类:- 算术仿函数
- 关系仿函数
- 逻辑仿函数
用法:- 这些仿函数所产生的对象,用法和一般函数完全相同
- 使用内建函数对象,需要引入头文件
功能描述:
- 实现四则运算
- 其中negate是一元运算,其他都是二元运算
仿函数原型
- template plus //加法仿函数
- template minus//减法仿函数
- template multiplies//乘法仿函数
- template divides//除法仿函数
- template modulus //取模仿函数
- template negate//取反仿函数
//1.取反仿函数 void test1() { negaten; std::cout< p; std::cout< 4.3.3 关系仿函数
仿函数原型:
- templatebool equal_to //等于
- templatebool not_equal_to //不等于
- templatebool greater //大于
- templatebool greater_equal //大于等于
- templatebool less // 小于
- templatebool less_equal //小于等于
#include#include #include #include using namespace std; void test() { vector v; v.push_back(10); v.push_back(30); v.push_back(40); v.push_back(20); sort(v.begin(),v.end(),greater ()); for(vector ::iterator it= v.begin() ;it!=v.end();it++) { cout<<*it<<" "; } cout< 4.3.4 逻辑仿函数 函数原型:
- template bool logical_and //逻辑与
- template bool logical_or //逻辑或
- template bool logical_not //逻辑非
5.STL-常用算法
概述:
5.1 常用遍历算法
- 算法主要由头文件 、、组成
5.1.1 for_each
- for_each //遍历容器
- transform //搬运容器到另一个容器中
函数原型:
- for_each(iterator beg,iterator end,_func);
//普通函数 void myPrint(int v) { cout<v; for(int i=0;i<5;i++) v.push_back(i); for_each(v.begin(),v.end(),myPrint); cout< 5.1.2 transform 功能:
- 将一个容器的内容搬运到另一个容器
函数原型:- transform(iterator beg1,iterator end1,iterator beg2,_func)
- beg1 源容器开始迭代器
- end1 源容器结束迭代器
- beg2 目标容器开始迭代器
- _func 函数或者函数对象(仿函数)
class MyTransform { public: int operator()(int v) { return v; } }; void test() { vector5.2 常用的查找算法v; for(int i=0;i<5;i++) { v.push_back(i); } vector vTarget; vTarget.resize(v.size());//一定要开辟空间,不能用reserve transform(v.begin(),v.end(),vTarget.begin(),MyTransform()); for(vector ::iterator it = v.begin();it!=v.end();it++) { cout<<*it<<" "; } cout< ::iterator it = vTarget.begin();it!=vTarget.end();it++) { cout<<*it<<" "; } } 5.3常用的排序算法
- find
- find_if
- adjacent_find
- binary_search
- count
- count_if
5.4 常用的拷贝和替换算法
- sort
- random_shuffle 随机打乱元素
- merge
- reverse
5.5 常用的算术生成算法
- copy
- replace
- replace_if
- swap
头文件:#include
5.6 常用的集合算法
- accumulate 计算容器内所有元素的总和
- fill 向容器内填充元素
- set_intersection 求两个容器的交集
- set_union 求两个容器的并集
- set_difference 求两个容器的差集
继续当一名咸鱼( ̄︶ ̄)!
Stay hungry,Stay foolish!欢迎分享,转载请注明来源:内存溢出
评论列表(0条)