C++命名空间

命名空间的作用是将可能名称相同的函数和类进行隔离,类似于Java中的package


C++控制符endl

控制符endl具备刷新输出功能,而换行符\n不具备


C++允许连续使用赋值运算符

  1. int x, y, z;
  2. x = y = z = 10; // 赋值从右往左执行,首先88被赋给z,然后将z的值赋值给y,然后将y的值赋值给x

C++中的short为16位,long为32位,long long 为64位,而int的大小是不固定的,随着机器字长的变化而变化


C++中建议使用static_cast<type>来进行类型强制转换,static_cast的转换更加严格


C++数组初始化的几种方式

  1. int arr[] = {1, 2, 3, 4}; // 自动计算arr个数
  2. int arr2[5] = {1, 2, 3, 4}; // 随后一个数为0
  3. int arr3[5] = {0}; // 所有的数都为0
  4. char arr4[] = {1, 2, 3, 4}; // 自动类型转换
  5. char arr5[]{1, 2, 3, 4}; // 自动类型转换
  6. //char arr5[] = {1,2,3,4, 9999999999}; // 不能自动类型转换

c风格字符串的拼接可以直接用空格隔开两个需要拼接的字符串常量,字符串变量不能这么做

  1. const char *str = "HelloWorld" "nihao";

C++字符处理库,cctype


在C++中,如果数据类型本身不是指针,则可以将const数据或非const数据的地址赋值给指向const的指针,否则只能将非const的数据赋值给非const指针,而const数据不可以(包括函数传参)。


C++中const的位置的意义

  1. int a = 10;
  2. const int *p = &a; // p指针无法修改a的值
  3. int* const p = &a; // p指针可以修改a的值,但是p只能指向a

C++的数组指针和指针数组

  1. int *arr[5]; // 5个int指针组成的数组,指针数组
  2. int (*arr)[5]; // 一个指向int数组的指针,数组指针,可以看成是int[5] *arr,也就是二维数组

C++函数指针

  1. int (*func)(int, int); //名为add的函数指针,
  2. //可以理解为int (int, int) *func,返回值为int,参数为两个int的函数指针

C++临时变量、引用参数和指针

C++的临时变量将出现在函数具有(常量?)引用参数,且传入的实参与引用参数的类型不一致时,会生成一个临时变量

  1. void swap(int &a, int &b) {
  2. int c = a;
  3. a = b;
  4. b = c;
  5. }
  6. long a = 5, b = 6;
  7. swap(a, b) //这种情况下,将生成临时变量,因为a,b的类型不匹配,而最终结果也将交换临时变量而导致a和b的值没有改变
  8. swap(5, 6) //这种情况没有意义,只用于说明。但是也是会生成临时变量,然后将a和b指向这个临时变量
  9. int c = 2;
  10. swap(c + 3, c + 5) //这种情况没有意义,只用于说明。但是也会生成临时变量,理由同上

C++的左值和右值


C++不能返回非堆内存的局部变量的引用


C++函数匹配机制

如果存在多个重载以及模板函数,匹配过程如下:

  1. 创建候选函数列表,其中包含所有函数名称与被调函数相同的函数
  2. 从候选函数列表选出可行函数列表,即参数数目都是正确的
  3. 确定是否有最佳的可行函数

函数匹配从最佳到最差的顺序如下:

  1. 完全匹配,但常规函数优先于模板
  2. 提升转换,数据类型提升例如char,short转换为int,float转换为double
  3. 标准转换,例如int转换为char,long转换为double,或者说强制类型转换
  4. 用户自定义转换

完全匹配表

从实参 到形参
type type &
type & type
type [] type *
type (参数列表) type (*)(参数列表)
type const type
type volatile type
type * const type *
type * volatile type *

且非const数据指针优先与非const指针参数匹配
非模板函数优先与模板函数(包括具体化)
显示具体化模板函数优先于模板函数


C++11的新关键字decltype


C++头文件常包含的内容

  1. 函数原型
  2. 使用#define或const生命的符号常量
  3. 结构声明
  4. 类声明
  5. 模板声明
  6. 内联函数

C++头文件管理,避免多次包含同一个头文件

  1. #ifndef HEADER //如果没有这个宏声明
  2. #define HEADER //则声明这个宏
  3. #include "header" // 然后导入头文件
  4. #endif
  5. // 这样如果有第二次,则可以避免多次包含

C++存储数据

  1. 自动存储持续性:在函数定义中声明的变量(包括参数)的存储持续性是自动的,在程序的开始执行其所属的函数或代码块时被创建,在执行完函数或代码块的时候,使用的内存就被释放。C++有两种存储持续性为自动的变量。
  2. 静态存储持续性:在函数定义外的变量和使用关键字static定义的变量的存储持续性都为静态。在整个程序允许过程中都存在。C++有三种存储持续性为静态的变量。
  3. 线程存储持续性(C++11):如果变量是使用关键字thread_local声明的,则其生命周期与所属线程一样长。
  4. 动态存储持续性:用new运算符分配的内存将一直讯在,直到使用dedlete运算符将其释放或程序结束为止。

C++外部变量


C++ 全局const变量与static变量一样,链接性是文件内部的


C++new运算符

使用这种特殊的new声明的指针,不能用delete释放内存,因为该new并没有重新分配内存,而只是使用了已有的内存。
而如果使用定位new运算符声明的对象,其需要显示调用析构函数,以确保对象被销毁。


C++命名约定


VS快捷键大全


C++ const成员函数


C++对象数组


C++成员声明时的初始化在C++11才提供


C++枚举类

C++枚举类还可以显示的指定底层枚举类型

  1. enum class: short pizza {
  2. Small,
  3. Medium,
  4. Large,
  5. XLarge
  6. }; //指定底层实现为short整形

C++友元

创建友元的步骤

  1. 在类中声明友元函数

    1. class Test {
    2. public:
    3. friend Test operator*(double d, const Test &t);
    4. }
  2. 定义友元函数,此步不用加上friend关键字

    1. Test operator*(double d, const Test &t) {
    2. //....
    3. }

C++ explicit关键字


C++转换函数


C++特殊成员函数


C++类继承

C++继承的子类中的方法如果有跟基类相同,则不管参数列表是否一样,都将覆盖基类函数。
C++继承的子类中的方法的返回类型如果是基类的对象引用或指针,则可以修改为子类的引用或指针

  • 公有继承:基类的公有成员将称为派生类的保护成员,基类的私有部分也将称为派生类的一部分,但只能通过基类的公有和保护方法方法
  • 保护继承:基类的公有成员将成为派生类的保护成员
  • 私有继承:基类的公有成员和保护成员都将成为派生类的私有成员

C++虚方法

如果子类具有一个函数签名和基类相同的函数,则调用的时候使用的是对象指针或引用,那么
如果方法是virtual的,则将产生多态行为,函数调用将动态绑定对象
如果方法不是virtual的,则将不产生多态,将只调用基类函数
如果不是对象指针或引用,则也不会产生多态
而且子类重写基类的虚方法,该方法最好也是virtual的
基类的析构函数必须是virtual的


C++虚函数表


C++纯虚函数


C++using

C++虚基类

虚基类是用关键字virtual声明继承的父类,即便该基类在多条链路上被一个子类继承,但是该子类中只包含一个该虚基类的备份,虚基类主要用来解决继承中的二义性问题,这就是是虚基类的作用所在。
正是由于虚基类的这个作用,所以在每个子类的构造函数中必须显示的调用该虚基类的构造函数,不管该虚基类是不是直接的父类。
其次,虚基类的构造函数的调用早于其他非虚基类的构造函数的调用。


C++类内存模型


C++模板(重点)


C++友元类(重点)


C++类型转换函数

  1. static_cast

static_cast的转换格式:static_cast(expression)
将expression转换为type-id类型,主要用于非多态类型之间的转换,不提供运行时的检查来确保转换的安全性

  • 用于类层次结构中,基类和子类之间指针和引用的转换;当进行上行转换,也就是把子类的指针或引用转换成父类表示,这种转换是安全的;当进行下行转换,也就是把父类的指针或引用转换成子类表示,这种转换是不安全的,也需要程序员来保证;
  • 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum等等,这种转换的安全性需要程序员来保证;
  • 把void指针转换成目标类型的指针,是及其不安全的;注:static_cast不能转换掉expression的const、volatile和__unaligned属性。
  1. dynamic_cast

dynamic_cast的转换格式:dynamic_cast(expression)
将expression转换为type-id类型,type-id必须是类的指针、类的引用或者是void *;如果type-id是指针类型,那么expression也必须是一个指针;如果type-id是一个引用,那么expression也必须是一个引用。

  • dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。在多态类型之间的转换主要使用dynamic_cast,因为类型提供了运行时信息。
  1. const_cast

const_cast的作用是用来改变表达式里面的常量性(const)或易变性(volatile)。

  • 常量指针被转化成非常量指针,并且仍然指向原来的对象;
  • 常量引用被转换成非常量引用,并且仍然指向原来的对象;
  1. reinterpret_cast
  • 用于位的简单重新解释
  • 滥用 reinterpret_cast 运算符可能很容易带来风险。 除非所需转换本身是低级别的,否则应使用其他强制转换运算符之一。
  • 允许将任何指针转换为任何其他指针类型(如char到int或One_class到Unrelated_class之类的转换,但其本身并不安全)
  • 也允许将任何整数类型转换为任何指针类型以及反向转换。
  • reinterpret_cast 运算符不能丢掉 const、volatile 或 __unaligned 特性。
  • reinterpret_cast 的一个实际用途是在哈希函数中,即,通过让两个不同的值几乎不以相同的索引结尾的方式将值映射到索引。
  1. bad_cast
  • 由于强制转换为引用类型失败,dynamic_cast 运算符引发 bad_cast 异常。

C++虚函数

  • 普通函数(非类成员函数)不能是虚函数
  • 静态函数(static)不能是虚函数
  • 构造函数不能是虚函数(因为在调用构造函数时,虚表指针并没有在对象的内存空间中,必须要构造函数调用完成后才会形成虚表指针)
  • 内联函数不能是表现多态性时的虚函数
  • 模板类中可以使用虚函数
  • 一个类(无论是普通类还是类模板)的成员模板(本身是模板的成员函数)不能是虚函数

    C++右值引用


C++指针的指针,指针的引用


C++私有静态成员指针的释放时机?


C++静态成员变量的初始化

C++ 的静态成员不能在类内初始化,要在类外进行初始化。即便是null,也是要初始化的,而且必须初始化。否则会报错,无法引用的变量。

  1. class static_class
  2. {
  3. private:
  4. static int *s;
  5. public:
  6. static_class() = default;
  7. ~static_class() = default;
  8. };
  9. int *static_class::s = nullptr;

但是C++的静态成员函数和普通函数是一样的。

  1. class static_class
  2. {
  3. private:
  4. static int *s;
  5. public:
  6. static_class() = default;
  7. ~static_class() = default;
  8. static void say_hello();
  9. };
  10. void static_class::say_hello()
  11. {
  12. std::cout<<"Hello"<std::endl;
  13. }

C++ double free detected问题