C++复习学习笔记

复习到的知识点

1. C++三种编程方式

  1. 面向过程编程
  2. 面向对象编程
  3. C++模板支持的泛型编程

2. 注释

C++的注释为//,C的为/*...*/,C++可以识别/*...*/,C99将//注释加入到C标准

3. 名词空间

1
2
using std::cout;        //只将std名字空间下的cout暴露出来
using namespace std; //将std名字空间的下所有标识符暴露出来

4. 函数

  1. 函数头 指出函数返回值的类型和函数期望通过参数传递给它的信息的类型
  2. 函数体 由花括号中的C++语句组成

5. C++语句

  1. 声明语句 定义变量的名称和类型
  2. 赋值语句 使用赋值运算符给变量赋值
  3. 消息语句 将消息发送给对象,激发某种行动
  4. 函数调用 执行函数。被调用的函数执行完毕后,程序返回到函数调用语句后面的语句
  5. 函数原型 申明函数的返回类型、函数接受的参数数量和类型
  6. 返回语句 将一个值从被调用的函数返回到调用函数中

6. C++命名规则

  1. 在名称中只能使用字母字符、数字和下划线
  2. 名称的第一个字符不能是数字
  3. 区分大写字符和小写字符
  4. 不能将C++关键字作为名称
  5. 以两个下划线或下划线和大写字母开头的名称被保留给实现(编译器和其使用的资源)使用。以一个下划线开头的名称被保留给实现,用作全局标识符
  6. C++对于名称的长度没有限制,名称中所有字符都有意义,但有些平台有长度限制(和C标准有所区别C标准只保证前63个字符有意义 ps:gcc6.3.1下使用长度超过63且前63个字符相同的两个变量测未出现错误)

7. climits

头文件定义了符号常量表示类型的限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <climits>

using namespace std;

int main(int argc, char *args[])
{
// g++ 6.3.1 fedora25 64位 linux
cout<<sizeof LLONG_MAX<<endl; //8
cout<<sizeof SHRT_MAX<<endl; //4
cout<<sizeof INT_MAX<<endl; //4
cout<<sizeof LONG_MAX<<endl; //8
return 0;
}

8. const与#define优先使用const

  1. const可指定类型
  2. const可设定作用域
  3. cosnt可用于更复杂的类型

9. C++中const变量可用来申明数组长度(C语言不可以)

1
2
3
//使用g++可以编译通过,使用gcc会报错
const int arraylen = 10;
int arr[arraylen] ={0};

10. 基础类型,整数和浮点数

11. 类型转换

  1. 初始化和赋值进行的转换
  2. 以{}方式初始化时进行的转换(C++11)
  3. 表达式中的转换
  4. 传递参数时进行的转换
  5. 强制类型转换
1
2
3
(typename) value;       //C语言风格
typename(value); //C++
static_cast<typename> (value); //C++四种强制类型转换运算符,static_cast是其中一种可用于将数值类型转换为另一种数值类型,比传统强制类型转换更严格

12. C++11中auto申明,使用关键字auto,不指定变量类型,编译器将把变量的类型设置成与初始值相同

13. 结构体struct、共用体union、枚举enum

14. 位字段,可使用按位运算替代这种方法

1
2
3
4
5
6
7
struct torgle_register
{
unsigned int SN : 4;
unsigned int : 4; //表示有4个位未使用
bool goodIn : 1;
bool goodTorgle : 1;
};

15. 使用new和delete应遵守的规则

  1. 不要使用delete来释放不是new分配的内存
  2. 不用使用delete释放同一块内存块两次
  3. 如果使用new[]为数组分配内存,应使用delete[]来使用
  4. 使用new[]为一个实体分配内存,则应使用delete(没有方括号)来释放
  5. 对空指针使用delete是安全的

16. C++三种管理数据内存的方式

  1. 自动存储,栈上的临时变量
  2. 静态存储,整个程序执行期间都存在,static变量,全局变量
  3. 动态存储,堆,new和delete

17. STL模板类vector是动态数组的替代品,C++11模板类array是定长数组的替代品

18. 类型别名

  1. 预处理器
    1
    #define BYTE char

编译阶段用char替换所有的BYTE

  1. typedef(C++、C关键字)
    1
    typedef char BYTE;

typedef不会创建新类型,只是为已有类型创建新的名称。

19. 数组指针、指针数组

1
2
int (*p)[4];    //数组指针,指向一个数组的指针
int *q[3]; //指针数组,包含指针的数组

20. 函数指针,是一个指针,指向一个函数

21. 内联函数

inline关键字,类联函数将使用相应函数代码替换函数调用,程序无需跳转到另一位值执行代码,提高运行速度,但需要占用更多内存,内联函数只是对编译器的建议,如果函数定义占用太多行,则不会被内联。C语言使用#define可以实现函数内联,但由于#define只是代码替换会引发问题,所以应尽量使用inline。

  1. 函数声明前加上关键字inline
  2. 函数定义前加上关键字inline

22. 左值引用&

1
2
3
int a = 0;
int &b = a; //b成为a的一个别名,对b的操作相当于操作b int *const b = &a;
b = 10; //*b = 10;

23. 默认参数

对于带参数列表的函数,必须从右向左添加默认值。即,要为某个参数设置默认值,其右边的所有参数都必须提供默认值。

1
int func(int x, int y, int z=0, int w=0);

24. 函数重载

函数重载的关键是函数的参数列表,称为函数特征标,和其返回值无关,和参数列表中参数的变量名也无关。
编译器在检查函数特征标时,把类型引用和其本身视为同一个特征标。

25. 函数模板

1
2
3
4
5
6
7
8
template <typename Type>
void Swap(Type &a, Type &b)
{
Type temp;
temp = a;
a = b;
b = temp;
}

模板函数也可以重载
函数模板特化

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef struct Job{
Job & operator =(Job const &r)
{
return *this;
}
}Job;
template <>
void Swap(Job &a, Job &b)
{
Job temp(a);
a = b;
b = temp;
}

函数模板偏特化是不可以的,可以使用函数重载实现

26. 说明符和限定符

  1. auto C++11之前在申明中使用,指明变量为自动变量,但在C++11中auto用于自动类型推断

    1
    2
    auto int x = 10;    //C++11中编译错误,C中可编译通过  指明x为自动变量即存放在栈上
    auto y = 10; //C++11中编译通过
  2. register 声明将变量存储到寄存器中,C++11中只是显示指出变量是自动的

  3. static
    1. 限制全局变量或函数的作用域
    2. 申明某个变量为静态变量
    3. 用于类函数
  4. extern 声明是引用声明,即声明引用在其它地方定义的变量
  5. mutable 即使变量所在的类或结构体变量为const,其用mutable修饰的变量仍可被修改
  6. const 被其修饰的变量不可改变
  7. volatile 禁止编译器对某个变量进行优化,每次都去内存中取值,不使用寄存器中的缓存值

27. new、delete的用法

  1. 常用的用于创建对象并初始化的new,如果失败抛出异常std::bad_alloc

    1
    2
    string *str = new string("abc");
    int *arr = new int[4];
  2. 常用的用于创建对象并初始化的new,如果失败不抛出异常返回NULL

    1
    2
    int *arr1 = new() int[10000000000];             //抛出std::bad_alloc
    int *arr2 = new(nothrow) int[10000000000]; //不抛出异常,返回NULL
  3. 用于申明空间,类似malloc

    1
    void *p = operator new(10000000000 * sizeof(int));    //失败抛出std::bad_alloc
  4. 在已有空间上构建对象

    1
    2
    void *p = operator new(sizeof(int));
    int *a = new(p) int(10);
  5. 析构对象并释放空间

    1
    2
    3
    4
    string *str = new string("abc");
    delete str;
    int *arr = new int[4];
    delete[] arr;
  6. 释放空间

    1
    2
    void *p = operator new(10 * sizeof(int));    //失败抛出std::bad_alloc
    operator delete p;

28. 名字空间(名称空间、命名空间)namespace

29. 类 class

多态、继承、封装和数据隐藏、代码可重用、抽象

  1. 访问控制 public protected private
  2. 方法 内联方法(inline)
  3. 构造方法、析构方法、拷贝构造函数、等号运算符重载函数、取址运算符重载函数、取址运算符重载函数const版本(编译器会自动生成它们的默认版本)
  4. C++11列表初始化也可用于类
  5. const成员函数 函数内部不可修改成员变量的值,const修饰的变量和非const变量都可调用,const修饰的变量无法调用非const成员函数
  6. this指针,在成员函数中默认传入
  7. static修饰的成员函数,不会传入this指针,只能访问类中staitc成员变量

30. 运算符重载

重载限制

  1. 重载后的运算符必须至少有一个操作数为用户自定义类型,防止用户为标准类型重载运算符。
  2. 使用运算符不能违反运算符原来的句法规则。不能改变优先级
  3. 不能创建新运算符。
  4. 不能重载部分运算符。
    1. sizeof
    2. . 成员运算符
    3. .* 成员指针运算符
    4. :: 作用域解析运算符
    5. ?:
    6. typeid
    7. const_cast
    8. dynamic_cast
    9. reinterpret_cast
    10. static_cast
  5. 部分运算符只能通过成员函数进行重载。=()[]->

31. 友元 friend

让外部的函数、类、成员方法可以访问本类的私有变量和方法

  1. 友元函数
  2. 友元类
  3. 友元成员函数

32. 类的自动转化和强制类型转换

  1. 隐式类型转换,在只接受一个参数的构造方法前使用关键字explicit限定,可禁止隐式类型转换
  2. 显式类型转换
  3. 将类对象转化为其他类型,转换函数,特殊类成员运算符函数

33. 类构造函数初始化列表

类构造函数初始化列表初始化成员变量,顺序与初始化器中的排列顺序无关,与出现在类申明中的顺序相同。

34. 继承

继承是is-a的关系
派生类继承基类的方法和属性

  1. 公有继承
  2. 保护继承
  3. 私有继承

虚方法、纯虚方法
虚析构函数,确保正确的析构函数序列被调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;

class Basic
{
public:
Basic() { cout << "Basic" << endl; }
virtual ~Basic() { cout << "~Basic" << endl; } //如果不使用virtual pB在析构时将不会调用~Child
};

class Child : public Basic
{
public:
Child() { cout << "Child" << endl; }
virtual ~Child() { cout << "~Child" << endl; }
};

int main()
{
Child *pC = new Child();
Basic *pB = dynamic_cast<Basic*>(pC);
delete pB;
return 0;
}

虚方法会被加入到虚函数表中。如果派生类提供了虚函数的新定义,则虚函数表将保存新函数的地址。当派生类对象调用虚函数时就会调用派生类的新定义的虚函数了。
非虚函数在编译时即可得到其调用函数的地址,虚函数则要在运行时才去找需要调用函数的地址。
有纯虚方法的类,不能用来创建对象。
C++支持多重继承。
虚继承。避免多重继承继承了多个基类对象的问题。

35. 模板类

  1. 正常模板类
  2. 类的特化
  3. 类的偏特化

36. 异常

1
2
3
4
5
6
7
8
9
try
{
...
}
catch(exception & ex) //捕获异常
{
... //处理异常
throw ex; //抛出异常
}

37. RTTI运行阶段类别识别

  1. 如果可能的话,dynamic_cast运算符将使用一个指向基类的指针来生成一个指向派生类的指针,否则返回空指针。
  2. typeid运算符返回一个指出对象的类型的值。
  3. type_info结果存储有关特定类型的信息。

只能将RTTI用于包含虚函数的类层次结构,因为只有对于这种类层次机构,才能将派生对象的地址赋给基类指针。RTTI只适用于包含虚函数的类。

38. 类型转化运算符

  1. dynamic_cast 在类层级结构中进行向上转换,而不允许其他转换
  2. const_cast 改变值为const或volatile
  3. static_cast
  4. reinterpret_cast

新学的C++11新知识

1. C++11初始化方式

1
2
int a1 = {1, 2, 3}; //花括号用于数组、结构体初始化
int x1 = {24}; //C++98也可用于单值变量

C++11 新方式

1
2
3
4
int x2{7};      //变量初始化为7
int x3 = {7}; //变量初始化为7
int x4{}; //变量初始化为0
int x5 = {}; //变量初始化为0

C++11使得可将大括号初始化器用于任何类型,称为通用的初始化语法。
列表初始化禁止缩窄转换

1
2
3
4
5
6
7
8
double d1[4] {1.0, 2, 3, 4.0};
double d2[100] {}; //默认初始化为0
long l3[] = {20, 30, 2.3}; //C++编译错误,从double无法转化为long C语言编译成功,2.3转化为2
char str1[] = {"hello C++11"};
char str2[] {"hello C++11"};
string str3 = {"hello C++11"};
string str4 {"hello C++11"};
vector<int> vi{1, 2, 3, 4, 5}; //C++11中可以对vector使用列表初始化

2. C++11新增基于范围的for循环

1
2
3
4
5
6
7
8
9
double arr[]{1, 2, 3, 4, 5,};
for(double x:arr)
{
cout<<x<<endl;
}
for (auto x : {23, 1, 45, 23})
{
cout<<x<<endl;
}

3. 右值引用&&

1
int &&rr = 1;       //int const &rr = 1;

4. 关键字decltype

从表达式推断其结果类型。

1
2
3
4
5
6
7
8
9
10
11
template <typename T1, typename T2>
void sum(T1 x, T2 y)
{
type z = x + y; //type无法确定
}

template <typename T1, typename T2>
void sum(T1 x, T2 y)
{
decltype(x+y) z = x + y; //使用关键字解决
}

5. 后置返回类型

1
2
3
4
5
6
7
8
9
10
11
template <typename T1, typename T2>
type sum(T1 x, T2 y) //type无法确定
{
return x + y;
}

template <typename T1, typename T2>
auto sum(T1 x, T2 y) -> decltype(x+y) //通过后置返回类型解决
{
type z = x + y;
}

6. thread_local关键字

声明变量的生命周期和所在线程相同。

7. 作用域内枚举

1
2
3
4
enum XX {one, two};
enum YY {haha, one}; //编译错误 one重复定义
enum class XX {one, two};
enum class YY {haha, one}; //编译成功

作用域内枚举不能隐式转化为整型,需要显式类型转化
作用域内枚举可指定底层类型

1
enum class : short XX {one, two};   //将底层类型设为short

8. nullptr空指针

C++11引入新关键字nullptr表示空指针。

9. C++11允许类内初始化,和在构造函数中使用初始化列表等价

1
2
3
4
5
6
class Test
{
public:
int a = 10;
int b = 20;
};

9. 模板别名

1
2
3
4
5
6
7
template <typename T>
using arrtype = std::arry<T, 12>;
array<double> gallones;
array<int> days;
arrtype<std::string>months;

using BYTE = char; //using 用于非模板和typedef等价

10. Lambda函数

1
[ capture ] ( params ) mutable exception attribute -> ret { body }
  1. mutable 修饰符说明 lambda 表达式体内的代码可以修改被捕获的变量,并且可以访问被捕获对象的 non-const 方法
  2. exception 说明 lambda 表达式是否抛出异常(noexcept),以及抛出何种异常,类似于void f() throw(X, Y)。
  3. attribute 用来声明属性。
  4. capture 指定了在可见域范围内 lambda 表达式的代码内可见得外部变量的列表
  5. params 指定 lambda 表达式的参数。
    1
    2
    3
    int x = 10;
    auto func = [x] (int y) mutable -> int { return x > y ? x :y;};
    cout<<func(30)<<endl;

11. 包装器

参考资料

  1. C++PrimerPlus(第六版)中文版