命名规范
- 函数名第一个字母大写,其后每个单词字母开头大写,驼峰式
- 变量名小写,单词之间下划线连接
- 类名,驼峰式。
第一部分:基础
- “\t”:水平制表符,占8个字符的位置, 若该字符前的字符不满8个,该字符补齐剩下的空位,满8个以上的,去除8倍数的字符,再补齐剩下的空位。例:
"123456\t"="123456__"('_'代表一个空格,此处两个空格)
,"12345678\t"="12345678________"
。 - 数组地址:数组名代表第一个数组元素的地址,
&[数组名]
代表整个数组的地址。 - 宏与内联函数相关。
- 为函数参数添加默认值,方向必须是从右向左
- 具体化函数模板,针对于特殊类型的操作:
template <> [返回类型][函数名]<特殊类型>(参数列表)
,注:普通函数模板并非定义,具体化函数模板才是具体类型的函数定义
- 函数模板不能作为其他函数的参数,具体化的函数模板才能作为参数传递
- 使用名称空间中声明的变量,而不是使用全局变量
nullptr
表示空指针:int* p = nullptr
- 定位new运算符:
new(指针名) classname
,将在指针所指向的空间中对class进行内存分配。 - 内联函数不需要寻址,当编译器执行到内联函数时直接将其展开,故内联函数以简洁为主。
嵌套模板:
1
2template<typename T>
template<typename U>将模板作为参数:
template<template<typename T> class [参数名]>
,其中的参数必须是一个模板类。- 字符串的输入:
cin
:接受一个单词(用空格或换行符区分),会将换行符留在输入队列中getline()
:接受一行输入,以回车键为标志停止读取,且不保存换行符get()
:与前者功能相同,不同的是会将换行符留在输入队列中,解决办法是get([字符串名], [大小]).get()
(或分开成两行,后者读取输入队列中的换行符)
- 左值-变量;右值-常量或临时对象。左值引用不能指向右值,常左值引用可以指向右值(函数重载时,右值参数优先寻找右值引用,其次寻找常左值引用)
- 静态内容,将只在所声明的文件中可见
函数指针:
[返回类型] (*指针名)([参数列表])
,使用函数指针调用函数:fun()或(*fun)()
都是可用的1
2
3
4
5
6
7
8
9
10void (*p1)() = add; // void add() {}
void (**p)() = &p1; // 二级函数指针指向一级函数指针的地址。
(*(*p))(); // 调用add
///////////////
int (**p1) () = new(int(*[2])()); // 函数指针数组
p1[0] = add;
cout << p1[0](); // 等于func()的调用形式
cout << (*p1[0])(); // 等于(*fun)()的调用形式
cout << (*(*p1[0]))(); // 等于(*fun)()的调用形式
... // 可以添加多个(*)等价于(*fun)()的调用形式string
类:s1.append(s2)
:字符串拼接(修改了s1),返回s1s1.at(pos)
: 返回s1中位于pos的字符find
: 具有多个参数,寻找字符replace
第二部分:面向对象
- 创建对象:
ClassName object_name([参数列表])
或者列表初始化:ClassName object_name = {[成员属性], }
this
:是指向调用对象的指针- 类的友元函数是非成员函数,但其访问权限与成员函数相同。友元一般用于多个操作数的运算,且其中会使用到类的私有成员。
复制构造函数:
ClassName(const ClassName&)
,默认复制构造函数逐个复制成员的值(浅复制),对应的(深度复制):复制成员产生新的副本- 新建一个对象,并将其初始化为已有对象时
- 函数按值传递对象时(相当于创建原始变量的一个副本)
- 函数返回对象时(返回引用则不会调用,效率更高)
先创建的对象后析构,后创建的对象先析构。析构函数的调用时机:在当时代码块结束时,析构在该代码块中定义的对象
- 静态类成员函数:
- 对象不能调用
- 函数中只能使用静态成员
- 类成员初始化列表:
Constructor():mem1(m1), mem2(m2), mem(m3) {...}
,初始化的顺序为他们被声明的顺序 - 基类先被初始化,再初始化派生类
- 基类指针或引用可以指向派生类,但不能使用派生类的方法
- 虚函数
virtual
:成员函数将根据引用或指针指向的对象类型是派生类还是基类来选择对应的方法 protected
保护成员:派生类的成员可以直接访问保护成员,而不能访问私有成员- 包含纯虚函数的类不能被创建对象,即是抽象基类:
virtual void fun()=0
- 使用
explicit
防止单参数构造函数的隐式转换。 - 虚基类:当多个类继承同一个虚基类时,只产生一个副本。而非虚基类会为每个派生类产生一个副本。
- 模板类:
template<class T>
- 模板类的约束模板友元函数会在模板类中使用类的模板,非约束模板友元函数只使用新的模板
- 友元类:
friend class [classname]
,友元类的所有方法都可以访问原始类的私有成员和保护成员 前向声明:
class [需要使用友元函数的类];
,防止友元函数的类与需要友元函数的类产生编译时的循环依赖。正确的顺序如下:1
2
3
4
5
6
7class [使用友元函数的类]; // 前向声明
class [友元函数的类] {} //其中包含对另一个类私有或共有成员的使用
class [使用友元函数的类]
{
friend void [友元函数的类限定]::[函数名]([参数列表]);
...
} // 包含友元函数的定义throw
抛出的异常被catch
捕获,并且抛出的异常类型与捕获的异常类型所匹配- 将基类异常放在
catch
层次的最后一层 - 类一旦定义了含有参数的构造函数,则将删掉类的默认无参构造函数
- 类和结构体中的静态成员要在外部初始化,因为静态成员不属于某一个实例
- const对象只能调用const成员函数,且const成员函数不能修改对象属性。若要修改属性,则需将属性设为mutable
第三部分:高级特性
RTTI(运行阶段类型识别,动态类型转换)
dynamic_cast<Type *>([指针p])
: 如果p是Type或其派生出来的,将成功转化为Type类型的指针。否则结果为空指针typeid
运算符返回type_info
类,后者重载了==
和!=
运算符。
- 智能指针:自动删除不需要的分配的堆内存。无需调用delete
unique_ptr<class> [变量名](new class())
:只有一个智能指针可使用(没有复制构造函数)shared_ptr<class [变量名](new class())>
:采用引用计数来跟踪指针,计数为0才将其释放掉
- 函数适配器(
bind1st、bind2nd
):用于在二元函数于一元函数之间转换 迭代器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class iterator
{
public:
iterator(): pt(nullptr) { }
iterator(Node* pn): pt(pn) { }
double operator*() { return pt->item;} // 重载*运算符取值
iterator& operator++() // 重载前缀++运算符: ++it
{
pt = pt->next;
return *this;
}// it 指向下一个元素
iterator operator++(int) // 重载后缀++运算符:it++
{
iterator tmp = *this;
pt = pt->next;
return tmp;
}// 保存并使用原来的it所指的位置,但it本身已经指向下一个元素
}- 输入迭代器:来自容器的信息被视为输入
- 输出迭代器:传输给容器的信息被视为输出
- 正向迭代器:总是按照相同的顺序遍历值
- 双向迭代器:双向遍历容器
- 随机访问迭代器:直接跳到容器中的任何一个元素
- ostream_iterator, istream_iterator, reverse_iterator, front_insert_iterator, back_insert_iterator, insert_iterator
第四部分:C++11新特性
- 新类型:
long long
和unsigned long long
- 大括号用于初始化:
int x = {3}
、int *p = new int [4] {1, 2, 3, 4}
- 模板类
std::initializer_list
,可用于常规函数的参数 decltype(x) y
:让y的类型与x相同,其中x是一个表达式- 返回类型后置:
int add(double, int) = auto add(double, int) -> int
- 创建别名:
using []=[]
- 类成员的初始化
- 新容器:
forward_list
、unordered_map
、unordered_mulitmap
、unordered_se
t、unordered_multiset
- 右值引用(右值为字面量):
int&& r = 12
- lambda表达式:
[可能要使用的外部变量](参数列表){语句块}
包装器(
include<functional>
):function<[返回类型]([参数类型列表])> 名称
,指向函数名、函数指针或lambda表达式。用于整合调用特征标相同的函数,即返回值和参数类型相同(例如让模板函数只实例化一次,缩小了可执行代码exe的规模)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
typedef function<double(double)> fdd;
template<typename T>
T use_f(T t, function<double(double)> f)
{
cout << "use_f.\n";
return f(t); // 在这里重载的括号运算符
}
class Fp
{
private:
double fp;
public:
Fp(double z = 1.0) : fp(z) { cout << "constructor.\n"; }
double operator()(double p) { cout << "operator().\n"; return fp * p; }
};
int main()
{
cout << use_f<double>(2.0, Fp(3.0));
}定义移动构造函数会导致赋值运算符重载被删除然后报错:
...operator=(...).. cant be referenced--it is a deleted function.
override
:指出函数要覆盖一个虚方法。final
:指出函数不能被重写可变参数模板:
Arg... arg
1
2
3
4
5
6
7
8
9
10
11
12template<typename T, typename Args...>
void func(T t, const Args&... args)
{
// 使用t
func(args...) // 递归调用,对剩下的参数解包
}
// 让递归终止的函数
template<typename T>
void func(T t)
{
// 使用t,不再递归调用func
}移动语义:
- 移动构造函数
classname(classname && c){...必须清空c的信息...}
- 移动赋值运算
classname& operator=(classname&& c){判断自我赋值...重置this指向对象的信息...return *this;}
- 强制移动:
(typename&&)左值变量
或std::move(左值变量)
- 移动构造函数
第五部分:STL
序列容器
vector<typename>
: 数组的类表示size()
:返回容器中元素数目end()
: 返回一个指向超尾元素(最后一个元素的下一个位置)的迭代器push_back(element)
: 将元素添加到容器末尾1
2
3
4
5
6
7
8
9
10
11for (int i = 0; i < 3; i++) // std::vector<Point> pts
{
std::cout << "capacity: " << pts.capacity() << std::endl;
pts.push_back(Point(1, 2, 3)); // 此操作会创建临时对象,并调用复制构造函数,复制到pts中。并且每一次添加都会动态扩充vector的大小,并将旧的值拷贝(调用复制构造函数)到新的值中
// 接着将临时对象析构
std::cout << "-----end capacity: " << pts.capacity() << std::endl;
}
/*
1. 若事先定好vector的大小,则不会在添加时发生复制操作。
2. 且push_back(class(..))与emplace_back(class(..))等效(都会发生拷贝操作),emplace_back(构造参数列表)即使用了可变参数模板,则不会发生拷贝操作,效率最好。
/*erase(指向开始位置, 指向结尾位置的下一个位置)
:删除指定区间的元素,参数为迭代器insert(插入位置,插入区间的起始位置,插入区间的结尾位置的下一个位置)
:将一个容器的区间内容插入到新的位置中for_each(开始位置, 结尾位置的下个位置, 函数)
random_shuffle(开始位置,结尾位置的下个位置)
sort(开始位置, 结尾位置的下一个位置,[布尔函数])
deque<typename>
:双端队列push_front(element)
:于队头添加元素push_back(element)
:于队尾添加元素
list<typename>
:双向链表merge(list x)
:将调用链表与链表x合并,连个链表必须有序。结果保存在调用链表中remove(const T& val)
:在链表中删除val的所有实例unique()
:将连续的相同元素压缩为单个元素
forward_list<typename>
:单链表queue
: 适配器类(底层类为deque
)empty()
size()
front()
:返回指向队首元素的引用back()
:返回指向队尾元素的引用push(cont T& x)
:在队尾插入xpop()
:删除队首元素
priority_queue
:适配器类(底层类是vector),最大的元素在队首stack
:适配器类(底层类为vector)empty()
size()
top()
:返回指向栈顶元素的引用push(const T& x)
:在栈顶部插入xpop()
: 删除栈顶元素
关联容器
将键与值关联起来的容器
set<typename>
:键与值相同,值就是键,可翻转,自动排序,键是唯一的lower_bound(key)
:将键作为参数,并返回一个指向集合中第一个不小于键参数的成员的迭代器upper_bound(key)
:将键作为参数,并返回一个指向集合中第一个大于键参数的成员的迭代器set_union(1.begin(), 1.end(), 2.begin(), 2.end(), 输出迭代器)
:求解两个集合的并集
multiset
: 允许又重复的值map
:底层结构是红黑树find(key)
: 寻找键为key的元素,找到则返回一个指向该位置的迭代器,否则返回map.end()
multimap<key_typename, value_typename>
:键与值不同,同一个键可能与多个值关联,自动排序equal_range(key)
:获取已给key的所有键值对,返回两个迭代器组成的pair,且该pair的second指向超尾
pair<const key_type, data_type>
:一个键值对对象,map中的键值对first
: 访问键second
:访问值
unordered container
: 底层是hashtable(类似于java)