C++语法笔记

71 阅读5分钟

static

static修饰的内容,其作用域不变,生命周期与程序相同,且在整个程序执行过程中,仅会被初始化一次

静态局部变量

  • 在实际开发中,若使用静态局部变量可以替代全局变量,优先使用静态局部变量。

静态成员

  • 把类的成员声明为静态后,则可以认为该成员独立于类,该成员仅在逻辑上属于该类。
  • 静态成员使用类名::静态成员就能够访问,不需要创建对象。

const

const修饰其左侧(若左侧为空则修饰右侧)内容,const修饰谁,谁就不能变

  • const表示只读,表达常量的语义使用constexpr

const与指针

  • char const * str(const char * str):const修饰类型,即类型的值不能改变,但指针的指向可以改变。
  • char * const strconst修饰指针,即类型的值可以改变,但指针的指向不能改变(引用的本质)。

const与成员函数

  • 在成员函数后添加const,则成员函数不能修改成员变量的值。
  • const不能修饰构造函数与析构函数。

指针

  • 请确保指针在使用前被正确初始化
  • 在实际开发中,尽可能避免使用裸指针

void指针

void*表示任意类型的指针。

  • 只关心地址,不在乎指针指向的内容时可以使用void指针。
  • void指针仅表示指针,不能解引用,需要转换为具体类型的指针。
  • 不能直接对void指针做delete操作。

函数指针

一般形式:数据类型(*指针变量名)(参数列表);

  • 使用函数指针可以实现对函数的回调。

指针与数组名

int main() {
    int arr[5];
    printf("%p\n%p\n", arr, &arr);

    int *pointer = new int[5];
    printf("%p\n%p\n", pointer, &pointer);
    return 0;
}

上面这段代码描述了指针与函数名的差异。执行这段代码可以得到如下结果:

0x7ffed71574e0 
0x7ffed71574e0 
0x603000000010 
0x7ffed7157520

在该例中,pointer&pointer的值不相同,其原因是,pointer是一个指针且指向数组的首地址,&pointer是存储指针的地址。但此处arr&arr的值却相同,这说明,数组名arr不是指向数组首地址的指针。为了讨论arr到底是什么,再来看下面一段代码。

int main()
{
    int arr[5] = { 1, 2, 3, 4, 5 };
    printf("%p\n", arr);
    printf("%p\n", &arr[0]);
    printf("%p\n", arr + 1);
    printf("%p\n", &arr[1]);
    printf("%p\n", ((int*)(&arr + 1) - 1));
    printf("%p\n", &arr[4]);
    return 0;
}

执行这段代码可以得到如下结果:

0x7ffdf366d3c0 
0x7ffdf366d3c0 
0x7ffdf366d3c4 
0x7ffdf366d3c4 
0x7ffdf366d3d0 
0x7ffdf366d3d0

在该例中,arr相当于&arr[0],而arr + 1就相当于&arr[1]。值得注意的是,(&arr + 1)-1的地址与&arr[4]的地址相同。这说明,&arr表示的是指向整块数组内存的指针,它的类型实际是行指针int(*)[5]

类的初始化能否使用memset?

  • 当类中存在虚函数时,不能使用。虚函数通过虚函数表中的指针实现,memset会将虚函数表中的指针置空,使得后续的函数调用出现异常。
  • 当类中存在其它类的对象时,不能使用。类中包含的对象会先于该类完成初始化,memset会将该对象清空。
  • 在对象内部清空对象自身是不安全的,不要这么做

友元

  • 语法:friend
  • 类型:全局友元函数、友元类、友元成员函数。
  • 友元不能被继承,友元关系是有向的

多态

调用派生类中的非虚函数

  • 语法:派生类指针 = dynamic_cast<派生类类型 *>(基类指针);
  • 检查虚函数表。如果转换成功,dynamic_cast返回对象的地址,如果失败,返回nullptr

模板

函数模板

  • 语法:template <typename T>
  • 在调用函数时,可使用<类型名>指定模板类型。
  • 虚函数和析构函数不能创建模板。
  • 匹配条件一致的前提下,普通函数 > 具体化模板 > 常规模板。
  • 分文件编写时,常规函数模板的定义和声明均要放在头文件中;具体化模板仅将声明放在头文件中,定义放在源文件中。
  • autodecltype()均能实现自动类型推导,不清楚返回值时,返回值可以使用auto

类模板

  • 语法:template <class T>
  • 类模板的成员函数只会在对象实例化后被创建。

C++11标准

左值与右值

  • 左值:表达式结束后仍存在的持久化对象。
  • 右值:表达式结束后就消失的临时对象。
  • 能取地址的为左值,不能取地址的为右值。
  • 右值引用(将右值转化为左值,给临时变量续命)语法:数据类型&& 变量名 = 右值
  • 模板中使用T&&可以同时接收两种引用。
  • 套一层std::forward<T>(参数)可以实现完美转发。

lambda表达式

  • 把函数名替换为[]捕获列表即可实现lambda表达式。
  • [&]表示引用捕获,[=]表示值捕获。
  • 值捕获时,相当于值被const修饰,不能对值进行修改。(可使用mutable关键字进行修改)。