C++

C++ 官网

C++官方参考手册1
C++官方参考手册2
GNU官方参考手册

C++ 编译

编译环境判断

1
2
3
4
5
6
7
8
9
10
11
12
// C++0X
#define __cplusplus 1
#define __GXX_EXPERIMENTAL_CXX0X__ 1
// C++11
#define __cplusplus 201103L
#define __GXX_EXPERIMENTAL_CXX0X__ 1
// C++14
#define __cplusplus 201402L
#define __GXX_EXPERIMENTAL_CXX0X__ 1
// C++17
#define __cplusplus 201703L
#define __GXX_EXPERIMENTAL_CXX0X__ 1

调用C语言代码

1
extern "C" { /* C代码 */ }

类和对象

继承

重载

多态

数据抽象和封装

接口

文件和流

异常

动态内存

模板

信号

多线程

Web

C++ 历史

从开始有:C++98,C++03,C++TR1,C++14,C++ 扩展,C++17,C++20。

C++98是C++标准第一版,C++03是第二版,与C++98几乎一样,没做什么修改。

C++11

新增nullptr来表示空指针。

auto 类型:

在C++11中,auto被赋予了新的含义,即自动推导出变量类型。例如:

1
2
auto name = "John";
auto age = 10;

这里,auto仅仅是一个占位符,在编译器期间它会被真正的类型所替代。或者说,C++ 中的变量必须是有明确类型的,只是这个类型是由编译器自己推导出来的。

auto常用于定义迭代器,泛型编程等。

1
2
3
4
5
6
7
8
9
10
11
12
// 迭代器
int main(){
vector< vector<int> > v;
auto i = v.begin(); //使用 auto 代替具体的类型
return 0;
}
// 泛型
template <typename T>
void func(void){
auto val = T::get();
cout << val << endl;
}

auto的使用也是有限制的:

  • 不能用作函数的形参;
  • 不能作用域类的非静态成员变量;
  • 不能定义数组;
1
auto str[] = url;  //arr 为数组,所以不能使用 auto
  • 不可作用于模板参数;
1
2
3
4
5
template <typename T>
class A{ };
void main(){
A<auto> C2; //错误
}

decltype:

decltype也是在编译使其进行自动类型推导的关键字,意为声明类型(declare type),用于某些auto无法使用的情况下。基本形式为:

1
decltype(exp) varname;

exp是一个普通的表达式,其结果必须是有类型的,不可是void。该关键字可以通过exp的类型定义varname的类型。具体推导规则如下:

  • exp没有被()包裹,那么则返回exp的类型;
  • exp()包裹,或是一个左值,那么则返回exp的引用类型;

二者的区别在于:

  • decltype会保留CV限定符(const/volatile),auto会在表达式的类型是指针或引用时才保留CV限定符;
  • decltype会保留引用类型,auto会抛弃引用类型;

返回值类型后置:

这一语法可以将decltypeauto结合起来完成返回值类型的推导。

1
2
3
4
5
6
7
8
9
10
11
12
// 例如 
template <typename T, typename U>
auto add(T t, U u) -> decltype(t + u)
{
return t + u;
}
// 例如
template <typename T>
auto func(T& val) -> decltype(foo(val))
{
return foo(val);
}

连续的右尖括号 >>

C++98/03中不允许连续的>>,在C++11中可以。

begin和end
不必使用x.begin(),而是可以直接调用begin函数:

1
sort(begin(v), end(v));

using 关键字

using覆盖了typedef的全部功能,并增添了一些新的特性,改善了对模板的支持。

1
2
3
4
5
6
7
8
9
// 重定义unsigned int
typedef unsigned int uint_t;
using uint_t = unsigned int;
// 重定义std::map
typedef std::map<std::string, int> map_int_t;
using map_int_t = std::map<std::string, int>;
// 指针
typedef void (*func_t)(int, int);
using func_t = void (*)(int, int);

模板默认参数

在C++98/03中,类模板可以有默认的模板参数,但是函数却没有。在C++11中,函数也可以用默认模板参数了。
函数在使用默认模板参数时,可以像普通函数一样调用。但是类必须加上<>
若要填充模板参数,则要遵循从右向左的方式填充。
模板参数也支持了变长。

元组:

1
2
auto p = make_pair(1, "C++ 11"); 
auto t2 = make_tuple(1, 2.0, "C++ 11", {1, 0, 2});

列表初始化

初始化的适用性大大增加了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 用于类
class Foo
{
public:
Foo(int) {}
private:
Foo(const Foo &);
};
int main(void)
{
Foo a1(123);
Foo a2 = 123;
Foo a3 = { 123 };
Foo a4 { 123 };
int a5 = { 3 };
int a6 { 3 };
return 0;
}
// 用在函数返回值上
struct Foo
{
Foo(int, double) {}
};
Foo func(void)
{
return { 123, 321.0 };
}

新的for循环

新的基于范围的for循环:

1
2
3
4
5
6
7
8
9
10
// 数组
for(auto n : arr)
{
std::cout << n << std::endl;
}
// KV, val 类型是 std::pair
for(auto& val : mm)
{
std::cout << val.first << " -> " << val.second << std::endl;
}

lambda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
auto f = [](int a) -> int { return a + 1; };
std::cout << f(1) << std::endl; // 输出: 2
// 也可以省略返回值类型
auto f = [](int a) { return a + 1; };
auto f = [] { return a + 1; };
// 捕捉一定范围内的变量
auto x2 = [=]{ return i_ + x + y; }; // OK,捕获所有外部变量
auto x3 = [&]{ return i_ + x + y; }; // OK,捕获所有外部变量
auto x4 = [this]{ return i_; }; // OK,捕获this指针
auto x5 = [this]{ return i_ + x + y; }; // error,没有捕获x、y
auto x6 = [this, x, y]{ return i_ + x + y; }; // OK,捕获this指针、x、y
auto x7 = [this]{ return i_++; };
// 存储lambda表达式
std::function<int(int)> f1 = [](int a){ return a; };
std::function<int(void)> f2 = std::bind([](int a){ return a; }, 123);

非受限联合体 Union

过去C++98不允许联合体成员是非POD类型,但是只会取消了这种限制。
POD数据类型是:

  • 没有自定义的构造函数,析构函数,拷贝构造,移动构造。
  • 没有虚函数和虚基类。
  • 非静态成员必须是public。
  • 类的第一个非静态成员类型与基类不同。
  • 类或结构体继承时,派生类有非静态成员,且仅包含静态成员的基类;基类有非静态成员,而派生类没有非静态成员。
  • POD 类型不能包含非 POD 类型的数据。
  • 所有兼容C语言的数据类型都是 POD 类型。
    也就是说,能用 C 的 memcpy() 等函数进行操作的类、结构体就是 POD 类型的数据。

这里

智能指针<memory>
这里包含了三种智能指针: shared_ptr unique_ptr weak_ptr。智能指针是个模板类,可以指定类型,传入指针通过构造函数初始化。也可以使用make_shared函数初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 多个指针指向相同的对象
std::shared_ptr<int> ptra = std::make_shared<int>(a);
std::shared_ptr<int> ptra2(ptra); //copy
// 操作
get() // 获取原始指针
use_count() // 引用计数


// 唯一拥有其所指对象
// 超出作用域后自动释放内存,不能拷贝和赋值
std::unique_ptr<int> uptr(new int(10));
std::unique_ptr<int> uptr2 = std::move(uptr); // 转换所有权
uptr2.release(); //释放所有权


// 协助shared_ptr工作,像旁观者那样观测资源的使用情况。
std::shared_ptr<int> sh_ptr = std::make_shared<int>(10);
std::weak_ptr<int> wp(sh_ptr);
// 操作
expired() // 等价于use_count()==0
lock() // 获得一个可用的shared_ptr对象, 从而操作资源


// 循环引用
// 为了避免循环引用,应该把其中一个指针设置为weak_ptr
class Parent {
private:
std::weak_ptr<Child> ChildPtr;
}
class Child {
private:
std::shared_ptr<Parent> ParentPtr;
}

右值引用
左值右值:左值指的是可以取地址的,有名字的,非临时的就是左值,右值指的则是不能取地址的,没有名字的,临时的就是右值。可见立即数,函数返回的值等都是右值;而非匿名对象(包括变量),函数返回的引用,const对象等都是左值。

左值引用:左值引用要求右边的值必须能够取地址,如果无法取地址,可以用常引用。

右值引用:右值引用用来绑定到右值,绑定到右值以后本来会被销毁的右值的生存期会延长至与绑定到它的右值引用的生存期。右值引用可以进行读写操作,而常引用只能进行读操作。

1
int &&var = 10;

Move/&&

通过move构造和move赋值可以避免内存重新分配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// move constructor
ArrayWrapper (ArrayWrapper&& other)
: _p_vals( other._p_vals )
, _size( other._size )
{
other._p_vals = NULL;
}

// copy constructor
ArrayWrapper (const ArrayWrapper& other)
: _p_vals( new int[ other._size ] )
, _size( other._size )
{
for ( int i = 0; i < _size; ++i )
{
_p_vals[ i ] = other._p_vals[ i ];
}
}

Bind: 可以把一个参数较多的函数给封装成参数较少的函数,重排源函数的参数顺序等

1
2
3
auto newFun = bind(oldFun,arg_list);
auto new_check_size = bind(check_size,std::placeholders::_1,n);
auto newFun = bind(old_Fun,_2,_1);

C++14

C++ 14 是 C++ 11 的增量更新,主要是支持普通函数的返回类型推演,泛型 lambda,扩展的 lambda 捕获,对 constexpr 函数限制的修订,constexpr变量模板化等等。

C++17

C++20

技术分层

基础部分:

  • C基础:数据结构,结构体,函数,递归等
  • 数据结构:链表,栈,队列,树,二叉树,B树,图
  • 系统编程:基本操作,Shell,进程通信

进阶部分:接口的设计和封装,模块的划分。

  • 日志
  • 配置文件
  • Linux/Win下IPC库
  • Socket库
  • 数据库统一访问

高级部分:

  • Win 项目案例
  • Linux 项目案例
  • Android/IOS 案例

算法

algorithm内涉及到元素本身的操作无非是比大小。

1
2
3
4
5
6
7
8
9
10
11
12
// C++ :: 全局函数 查找
auto p = ::find(c.begin(), c.end(), target);
if(p!=c.end()){}

// C++ 排序
::sort(c.begin(), c.end()); // 使用 RandomAccess 迭代器,因此不可用于链表

// 比较
::max(a, b);
::min(a, b);
::max(a, b, comp); // comp(a, b)
::min(a, b, comp);

BOOST

QT

OpenCV