5. 继承
-
写法:
class 类名 : public 父类名 { };
-
在子类中访问同名父类成员:
父类名::父类成员
-
在子类构造函数中显式调用父类构造函数:
//以初始化列表的方式调用父类构造函数 derived::derived(arg_derived-list):base(arg base-list){} 子类构造函数 父类构造函数
-
子类的析构函数被执行时,执行完子类的析构函数后,自动调用父类的析构函数。
-
构造函数执行顺序:先执行父类的构造函数,再执行成员对象类的构造函数,最后执行子类的构造函数
-
析构函数执行顺序:与构造函数相反
6. 多态
-
在类的定义中,前面有virtua关键字的成员函数就是虚函数。只有虚函数才能使用多态机制
-
virtual关键字只用在类定义里的函数声明中,写函数体时不用。构造函数和静态成员函数不能是虚函数
-
多态实现的原理:虚函数表
每一个有虚函数的类(或有虚函数的类的派生类)都有一个虚函数表,该类的任何对象中都放着虚函数表的指针(放在对象的头部)。虚函数表中列出了该类的虚函数地址。多出来的4个字节就是用来放虚函数表的地址的。
-
虚析构函数:
- 通过基类的指针删除派生类对象时,通常情况下只调用基类的析构函数。但是,删除一个派生类的对象时,应该先调用派生类的析构函数,然后调用基类的析构函数。
- 解决办法:把基类的析构函数声明为virtua。派生类的析构函数可以virtua|不进行声明
- 一般来说,一个类如果定义了虚函数,则应该将析构函数也定义成虚函数。或者,一个类打算作为基类使用,也应该将析构函数定义成虚函数。
-
纯虚函数和抽象类:
-
纯虚函数:没有函数体的虚函数
virtual void Print()=0;
-
包含纯虚函数的类叫抽象类。抽象类只能作为基类来派生新类使用,不能创建抽象类的对象
-
抽象类的指针和引用可以指向由抽象类派生出来的类的对象
-
在抽象类的成员函数内可以调用纯虚函数,但是在构造函数或析构函数内部不能调用纯虚函数。
-
如果一个类从抽象类派生而来,那么当且仅当它实现了基类中的所有纯虚函数,它才能成为非抽象类
-
7. 输入和输出模板
-
输出重定向:
freopen("test.txt","w",stdout);
//将标准输出重定向到test.txt文件 -
输入重定向:
freopen(“t.txt",“r”,stdin);
//cin被改为从t.txt中读取数据 -
判断输入流结束:
int x; while(cin>>x){} return 0; //如果是从文件输入,比如前面有freopen(“some.txt”,”r”,stdin);那么,读到文件尾部,输入流就算结束;如果从键盘输入,则在单独一行输入CtrI+Z代表输入流结束
-
istream类的成员函数
-
istream & getline(char* buf, int bufSize);
//读取bufSize-1个字符到buf里,遇到'/n'提前结束istream & getline(char* buf, int bufSize, char delim);
//读取bufSize-1个字符到buf里,遇到delim提前结束 -
bool eof();
//判断输入流是否结束 -
int peek();
//返回下一个字符,但不从流中去掉 -
istream & putback(char c);
//将字符c放回输入流 -
istream & ignore(int nCount =1,int delim=EOF )
从流中删掉最多nCount个字符,遇到EOF时结束。
-
-
流操纵算子:
-
使用流操纵算子需要#include
-
整数流的基数:流操纵算子dec(10进制),oct(8进制),hex(16进制)
-
指定输出浮点数的有效位数:
cout<<setprecision(5)
//可以连续输出 -
指定输出浮点数的小数点后的有效位数:
cout<<fixed<<setprecision(5)
-
设置域宽:
cin>>setw(4); cout<<setw(4);
//宽度设置有效性是一次性的,在每次读入和输出之前都要设置宽度。 -
用户自定义流操作算子:
ostream & tab(ostream & output){ return output<<'/t'; }
-
-
文件读写:
-
#include//包含头文件
-
ofstream outFile("clients.dat",ios::out|ios::binary);//创建文件 "clients.dat"要创建的文件的名字 ios::out输出到文件,删除原有内容。 ios::app输出到文件,保留原有内容,总是在尾部添加 ios:binary以二进制文件格式打开文件 //文件名可以给出绝对路径,也可以给相对路径。没有交代路径信息就是在当前文件夹下找文件
-
文件的读写指针:标识文件操作的当前位置,该指针在哪里,读写操作就在哪里进行
-
ofstream fout("a1.out",ios::app);//以添加方式打开 long location=fout.tellp();//取得写指针的位置 location=10; fout.seekp(location);//将写指针移动到第10个字节处 fout.seekp(location,ios::beg);//从头数 fout.seekp(location,ios::cur);//从当前位置数 fout.seekp(location,ios::end);/ation ifstream fin("a1.in",ios::ate);//以添加方式打开 long location=fin.tellg();//取得读指针的位置 location=10; fin.seekg(location);//将写指针移动到第10个字节处 fin.seekg(location,ios::beg);//从头数 fin.seekg(location,ios::cur);//从当前位置数 fin.seekg(location,ios::end);//从尾部数location,location可以为负值
-
-
字符文件读写
-
二进制文件读写
-
二进制文件修改(既读又写)
-
-
函数模板:
-
template <class T> void Swap(T & x,T & y){ T tmp=X; x=y; y=tmp; }
-
函数模板中可以有不止一个类型参数。
-
不通过参数实例化函数模板:
Inc<double>(4)
-
函数模板可以重载,只要它们的形参表或类型参数表不同即可。
-
在有多个函数和函数模板名字相同的情况下,编译器如下处理一条函数调用语句 1)先找参数完全匹配的普通函数(非由模板实例化而得的函数)。 2)再找参数完全匹配的模板函数。 3)再找实参数经过自动类型转换后能够匹配的普通函数。 4)上面的都找不到,则报错。 //匹配模板函数时,不进行类型自动转换
-
-
类模板:
-
写法和函数模板差不多
-
Pair<string,int> student("Tom",19);
//实例化出一个类Pair<string,int> -
编译器由类模板生成类的过程叫类模板的实例化。由类模板实例化得到的类,叫模板类。同一个类模板的两个模板类是不兼容的
-
函数模版可以作为类模板的成员
-
类模板的“<类型参数表>”中可以出现非类型参数
template <class T,int size>
-
8. 标准模板库STL(一)
-
string类:
-
string类是模板类
typedef basic_string<char> string;
-
使用string类要包含头文件
<string>
-
初始化:
string s1("Hello"); string month="March"; string s2(8,'x');//8个‘x’组成的字符串
-
不能用数字或者单字符初始化string类对象(无对应构造函数),但是可以用单字符赋值给string对象
-
string支持流读取运算符cin,支持getline函数
-
用assign成员函数部分复制
string s1("catpig"),s3; s3.assign(s1,1,3);//从s1中下标为1的字符开始复制3个字符
-
单字符复制:
- s2[5]=s1[3]='a';
- s1.at(0)
- 成员函数at会做范围检查,如果超出范围,会抛出out of range:异常,而下标运算符[]不做范围检查。
-
连接字符串:
- 用+运算符连接字符串
- 用成员函数append连接字符串
-
比较字符串:
-
用关系运算符比较string的大小:==,>,>=,<,<=,!=。返回值都是bool类型
-
用成员函数comparel比较string的大小,大于返回1,小于返回-1,相等返回0
s1.compare(1,2,s3,0,3) //s1从下标1开始的2个字符与s3从下标0开始的3个字符进行比较
-
-
子串:
s2=s1.substr(4,5);//下标4开始5个字符
-
交换string:成员函数swap
-
寻找string中的字符:
- 成员函数find(),rfind()。支持从指定下标开始查找
- 成员函数find_first_of(),find_last_of()。在s1中查找"abcd”中任何一个字符第一次出现的地方,如果找到,返回找到字母的位置,如果找不到,返回string::npos。
- 成员函数find_first_not_of()。在s1中从前向后查找不在“abcd”中的字母第一次出现的地方,如果找到,返回找到字母的位置
-
删除string中的字符:
- 成员函数erase(n)。去掉下标n及之后的字符
-
替换string中的字符:
-
成员函数replace()。
s1.replace(2,3,"haha"); //将s1中下标2开始的3个字符换成"haha"
-
-
在string中插入字符:
- 成员函数insert()。
s1.insert(5,s2);
//将s2插入s1下标5的位置
- 成员函数insert()。
-
转换成C语言式char*字符串:
- 成员函数c_str()。s1.c_str()返回传统的const char*类型字符串,且该字符串以'/0'结尾。
-
字符串流处理-字符串输入流istringstream
-
string input("Input test 123 4.7 A"); istringstream inputString(input); string stringl,string2; int i; double d; char c; inputString>>stringl>>string2>>i>>d>>c;
-
-
字符串流处理-字符串输出流istringstream
-
ostringstream outputString; int a=10; outputString<<"This "<<a<<"ok"<<endl; cout<<outputString.str();
-
-
-
STL概述:
-
容器:可容纳各种数据类型的通用数据结构,是类模板
-
1)顺序容器vector,deque,list 2)关联容器set,multiset,map,multimap 3)容器适配器stack,queue,priority_queue
-
-
迭代器:可用于依次存取容器中的元素,类似于指针
-
算法:用来操作容器中的元素的函数模板
-
对象被插入容器中时,被插入的是对象的一个复制品
-
顺序容器:
- vector:头文件
<vector>
。动态数组。元素在内存连续存放。随机存取任何元素能在常数时间完成。在尾端增删元素具有较佳的性能(大部分情况下是常数时间)。 - deque:头文件
<deque>
。双向队列。元素在内存连续存放。随机存取任何元素都能在常数时间完成(但次于vector)。在两端增删元素具有较佳的性能(大部分情况下是常数时间) - list:头文件
<list>
。双向链表。元素在内存不连续存放。在任何位置增删元素都能在常数时间完成。不支持随机存取。
- vector:头文件
-
关联容器:
-
简介:
-
元素是排序的
-
插入任何元素,都按相应的排序规则来确定其位置
-
在查找时具有非常好的性能
-
通常以平衡二叉树方式实现,插入和检索的时间都是O(logN)
-
set/multiset 头文件
<set>
set即集合。set中不允许相同元素,multiset中允许存在相同的元素。
-
map/multimap 头文件
<map>
map与set的不同在于map中存放的元素有且仅有两个成员变量,一个名为first,另一个名为second,map根据first值对元素进行从小到大排序,并可快速地根据first来检索元素。map同multimap的不同在于是否允许相同first值的元素。
-
-
-
容器适配器:
- stack:头文件
<stack>
。栈。是项的有限序列,并满足序列中被删除、检索和修改的项只能是最近插入序列的项(栈顶的项)。后进先出。 - queue:头文件
<queue>
。队列。插入只可以在尾部进行,删除、检索和修改只允许从头部进行。先进先出。 - priority_queue:头文件
<queue>
。优先级队列。最高优先级元素总是第一个出列
- stack:头文件
-
顺序容器和关联容器中都有的成员函数:
- begin 返回指向容器中第一个元素的迭代器
- end 返回指向容器中最后一个元素后面的位置的迭代器
- rbegin 返回指向容器中最后一个元素的迭代器
- rend 返回指向容器中第一个元素前面的位置的迭代器
- erase 从容器中删除一个或几个元素
- clear 从容器中删除所有元素
-
顺序容器的常用成员函数:
- front:返回容器中第一个元素的引用
- back:返回容器中最后一个元素的引用
- push_back:在容器末尾增加新元素
- pop_back:删除容器末尾的元素
- erase:删除迭代器指向的元素(可能会使该迭代器失效),或删除一个区间,返回被删除元素后面的那个元素的迭代器
- insert:插入元素,第一个参数为要插入的地方
-
迭代器:
- 定义一个容器类的迭代器:
容器类名::iterator 变量名;
常量迭代器const_iterator
反向迭代器reverse_iterator
- 访问一个迭代器指向的元素:
* 迭代器变量名
- 迭代器上可以执行++操作,以使其指向容器中的下一个元素。如果迭代器到达了容器中的最后一个元素的后面,此时再使用它,就会出错,类似于使用NULL或未初始化的指针一样。
-
- 定义一个容器类的迭代器:
-
算法:
- 算法就是一个个函数模板,大多数在
<algorithm>
中定义 - 算法通过迭代器来操纵容器中的元素。许多算法可以对容器中的一个局部区间进行操作,因此需要两个参数,一个是起始元素的迭代器,一个是终止元素的后面一个元素的迭代器。比如,排序和查找
- find()函数返回值是一个迭代器。如果找到,则该迭代器指向被找到的元素。如果找不到,则该迭代器指向终止元素的后面一个元素。
- 对数组应用find()函数时,数组名即为迭代器,指向数组头一个元素。
- 算法就是一个个函数模板,大多数在
-
-
vector:
- 创建二维数组:
vector<vector<int> > v;
- 创建二维数组:
-
deque:
- 所有适用于vectorl的操作都适用于deque。
- deque还有push_front(将元素插入到前面)和pop_front(删除最前面的元素)操作,复杂度是O(1)
-
list:
-
- splice: 在指定位置前面插入另一链表中的一个或多个元素,并在另一链表中删除被插入的元素
-
-
函数对象:
- 若一个类重载了运算符
()
,则该类的对象就成为函数对象 - 函数对象可以当作参数传入成员方法中
- greater是函数对象类模板,
greater<int>
是函数对象类,greater<int>()
是个临时函数对象
- 若一个类重载了运算符
9. 标准模板库STL(二)
-
set/multiset:
-
-
multiset <A>a
等价于multiset<A,less<A>>a
插入元素时,multiset会将被插入元素和已有元素进行比较。由于less模板是用<进行比较的,所以,这要求A的对象能用<比较,即适当重载了<
-
插入set中已有的元素时,会忽略插入
-
-
map/multimap:
-
-
若pairs为map模版类的对象,pairs [key]返回对关键字等于key的元素的值(second成员变量)的引用。若没有关键字为key的元素,则会往pairs里插入一个关键字为key的元素,其值用无参构造函数初始化,并返回其值的引用。
-
-
容器适配器:
-
stack:
-
stack是后进先出的数据结构,只能插入,删除,访问栈顶的元素。
-
stack上可以进行以下操作:
push插入元素 pop弹出元素 top返回栈顶元素的引用
-
-
queue:
- 同样也有push,pop,top函数。但是push发生在队尾;pop,top发生在队头。先进先出。有back成员函数可以返回队尾元素的引用
-
priority_queue:
- priority_queue通常用堆排序技术实现,保证最大的元素总是在最前面。即执行pop操作时,删除的是最大的元素;执行top操作时,返回的是最大元素的引用。
-
stack,queue,priority_queue都有:
empty()成员函数 用于判断适配器是否为空
size()成员函数 返回适配器中元素个数
-
10. C++11新特性
-
智能指针shared_ptr:
- 需要包含头文件
<memory>
- 语法:
shared_ptr<T> ptr(new T);
- ptr可以像指针一样来使用,即*ptr就是用new动态分配的那个对象,而且不必操心释放内存的事。
- 多个shared_ptr对象可以同时托管一个指针(通过赋值),系统会维护一个托管计数。当无shared_ptr托管该指针时,将delete该指针。
- shared_ptr对象不能托管指向动态分配的数组的指针,否则程序运行会出错
- 放弃托管:
ptr.reset()
- 需要包含头文件
-
foreach循环
-
无序容器(哈希表):
- 需要头文件<unordered_map>
- 用法和map一样,效率比map高,插入元素不排序
-
Lambda表达式:
-