最近用C++面试刷题,简单整理了一些心得和知识点,希望也能帮助到使用C++的你,有什么建议欢迎留言!
文章目录
类
虚函数 & 纯虚函数 & override
- 声明为virtual,从而在调用指向子类对象的基类指针时,会调用子类对象的相应方法;否则只会调用基类的方法
- 声明为virtual并在结尾加上 = 0,基类不实现该函数
- override不是一个强制的关键词,但是是一个良好的编程习惯,加了override,编译器会检查是否真的重写了相应的函数(如函数名,参数列表是否一致)
多态
- 静态多态 — 编译阶段,多通过函数重载(同名函数)和模板实现
- 动态多态 — 运行阶段,多通过虚函数
- 区别,什么时候将函数实现和函数调用关联起来
vtable,虚表
每个类(注意不是实例),拥有一个vtable指向该类的所有函数地址
C++11 smart pointer
weak_ptr是为了解决shared_ptr循环引用导致死锁资源不释放的问题
STL
vector
- 头文件:
#include <vector>
- 底层为数组
- 构造函数
- 常规 —
vector<int> a;
- 初始化 —
vector<int> a(4, 0)
,长度为4,全零
- 常规 —
- 访问
[index]
或者.at(index)
.data()
获得底层指针
- 插入
push_back
iterator insert(iterator, n)
- 删除
pop_back
— 删除最后一个iterator erase(iterator)
- 注意iterator的使用,比如insert和erase都是接收iterator为参数,并返回新的iterator(迭代的时候一定要注意更新iterator)
- begin & end — 获得开始和结尾的iterator
list
- 头文件:
#include<list>
- 底层为doubly-linked list
- 和vector很像,额外有push/pop_front/back(可以头插和尾插)
- remove — 删除某些具体元素(如:remove(89),删除数组里面所有等于89的元素)
stack
- 头文件:
#include <stack>
- empty — 是否为空
- size — 返回stack的大小
- push — 插入一个元素
- top — 返回最上面的元素
- pop — 单纯的删除最上面的元素,一般与top结合使用
- emplace — 构造并传入一个对象
- swap — 交换两个stack的内容
queue
- 头文件:
#include <queue>
- empty
- size
- front + back
- push — 插入一个新的元素
- pop — 单纯删除第一个元素,一般与front结合使用
priority_queue
- 头文件:
#include <queue>
- 底层是heap
- empty
- size
- top
- pop
- push
自定义一个Comparator并传入
class mycomparison
{
bool reverse;
public:
mycomparison(const bool& revparam=false)
{
reverse=revparam;
}
bool operator() (const int& lhs, const int&rhs) const
{
if (reverse) return (lhs>rhs);
else return (lhs<rhs);
}
};
std::priority_queue<int, std::vector<int>, mycomparison> pq;
map & unordered_map
- 头文件:
#include <map>
,#include <unordered_map>
- map底层为红黑树,unordered_map底层为哈希表
- map — TreeMap,有序
- unordered_map — HashMap,无序
std::map<char, int> myMap;
// 插入
myMap.insert(std::pair<char, int>('a', 1));
// 访问
cout << myMap['a'];
// 查找
if(myMap.find('b') != myMap.end()){
cout << "found";
}else{
cout << "not found";
}
// 等价于
if(myMap.count('b') > 0){
cout << "found";
}else{
cout << "not found";
}
// 正向遍历
std::map<char,int>::iterator it;
for (it=myMap.begin(); it!=myMap.end(); ++it){
std::cout << it->first << " => " << it->second << '\n';
}
// 反向遍历
std::map<char,int>::reverse_iterator rit;
for (rit=myMap.rbegin(); rit!=myMap.rend(); ++rit){
std::cout << rit->first << " => " << rit->second << '\n';
}
// 注意单纯的end()返回的是最后一个元素后面一个位置,要想获得最后一个元素,要用rbegin()
- unordered_map基本一样,除了没有rbegin(因为本身是无序的)
set & unordered_set
- 头文件:
#include <set>
,#include <unordered_set>
- set底层为红黑树,unordered_set底层为hash table
- insert
- erase
- count
- find
- 基本和map很像
pair
#include <utility>
- first + second
std::pair<std::string, double> product1;
product1.first = "hello";
product1.second = 3.3;
auto product2 = std::make_pair(10, 2);
tuple
#include <tuple>
get<index>(tuple)
std::tuple<int,char> mytuple (10,'a');
std::get<0>(mytuple) = 20;
auto t = std::make_tuple(10, 2, 'a', "test");
迭代器(iterator)
本质就是指针
容器类 | 插入 | 删除 |
---|---|---|
list | 所有迭代器不失效 | 有且仅有被删除的节点迭代器失效 |
vector | 如果size不变(没有扩容),则插入元素之后的所有迭代器都失效;否则全部失效 | 被删除节点之后的迭代器全部失效 |
set/map | 所有迭代器不失效 | 有且仅有被删除的节点迭代器才失效 |
常用头文件
-
#include <algorithm>
— sort排序 -
#include <functional>
— greater,less,greater_equal -
#include <sstream>
— stringstream-
可以理解为动态string(可以动态修改内容但不会触发拷贝)
-
std::stringstream ss; ss << 100 << ' ' << 200; int foo,bar; ss >> foo >> bar;
-
-
#include <utility>
— pair -
#include <math.h>
— floor, pow, ceil
常用snippet
排序(调用系统函数)
#include <algorithm>
sort(v.begin(), v.end()); // 递增
sort(v.begin(), v.end(), std::greater<int>()); // 递减
//自定义结构体排序
struct Node
{
int a;
int b;
};
bool cmp(Node x, Node y)
{
return x.a<y.a;
}
vector<Node> v;
sort(v.begin(), v.end(), cmp);
读取二维数组
class Node{
public:
int value;
bool visited;
Node() : value(0), visited(true){};
};
int main(void)
{
int row, col;
cin >> row >> col;
vector<vector<Node>> matrix(row, vector<Node>(col));
for(int i = 0; i < row; i++){
for(int j = 0; j < col; j++){
cin >> matrix[i][j].value;
}
}
// do whatever you want
return 0;
}
输入输出
- getline — getline(cin, line, ‘\n’),最后一个参数是分隔符,可用来分隔一行(逗号分隔)
- stringstream — items >> str,可用>>提取字符串或者数字,但是前提是空格分隔
逐行读取(每行有2个数字)
#include <iostream>
#include <sstream>
using namespace std;
int main(){
int a = 0;
int b = 0;
string line;
while(getline(cin, line, '\n')){
stringstream numbers(line);
numbers >> a >> b;
cout << a + b << endl;
}
return EXIT_SUCCESS;
}
不定行数,每行不定数量的数字(空格分隔),读取并求和
#include <iostream>
#include <sstream>
using namespace std;
int main(){
string line;
while(getline(cin, line, '\n')){
stringstream numbers(line);
int sum = 0, tmp;
while(numbers >> tmp){
sum += tmp;
}
cout << sum << endl;
}
return EXIT_SUCCESS;
}
不定行数,每行不定数量的字符串(逗号分隔),读取并排序输出
输入
a,c,bb
f,dddd
nowcoder
输出
a,bb,c
dddd,f
nowcoder
代码
#include<iostream>
#include<sstream>
#include<vector>
#include<algorithm>
using namespace std;
void parseLine(string line){
stringstream items(line);
vector<string> words;
string word;
while(getline(items, word, ',')){
words.push_back(word);
}
sort(words.begin(), words.end());
for(int i = 0; i < words.size(); i++){
cout << words[i];
if(i == words.size() - 1){
cout << '\n';
}else {
cout << ',';
}
}
}
int main(){
string line;
while(getline(cin, line, '\n')){
parseLine(line);
}
return EXIT_SUCCESS;
}
各种排序算法
快速排序
int partition(vector<int> &vi, int low, int up) {
int pivot = vi[up];
int i = low-1;
for (int j = low; j < up; j++) {
if(vi[j] <= pivot) {
i++;
swap(vi[i], vi[j]);
}
}
swap(vi[i+1], vi[up]);
return i+1;
}
void quickSort(vector<int> &vi, int low, int up) {
if(low < up) {
int mid = partition(vi, low, up);
//Watch out! The mid position is on the right place, so we don't need to consider it again.
//That's why below is mid-1, not mid! Otherwise it will occur overflow error!!!
quickSort(vi, low, mid-1);
quickSort(vi, mid+1, up);
}
}