[C++] C++知识拾遗
1.lambda
表达式形式:[函数对象参数] (操作符重载函数参数) mutable 或 exception 声明 -> 返回值类型 {函数体}
1)对象参数(即变量捕获条件)
空。不进行捕获。
=。lambda所在范围内的可见局部变量,并以值传递的形式传入。
&。lambda所在范围内的可见局部变量,并以引用传递的形式传入。
this。所在类中的成员变量。
ver。捕获变量ver,值传递。
&ver。捕获变量ver,引用传递。
=,&ver。变量ver按引用传递,其余值传递。
&, ver。 变量ver按值传递,其余引用传递。
注意: = 和 & 捕获的变量包括局部变量、类的成员变量、全局变量以及静态变量。
如下例:
int a=1; int main(){ int b=2; auto f=[&](){cout<<a<<" "<<b<<endl;}; f(); return 0; }
输出:
1 2
2)操作符重载函数参数
与一般函数声明类似,可省略。
3)mutable或exception声明
mutable与const相反,说明变量可被修改。exception则说明函数可能产生的异常类型。可省略。
4)返回值类型
若返回值为void,或者函数体中只有一处return(使得编译器可以推断返回值类型),则可省略。
5)函数体
函数内部的表达。
例子:
// 不指定返回类型 [] (int x, int y) { return x + y; } // 指定返回类型 [] (int x, int y) -> int { int z = x + y; return z; }
2. 迭代器
内容较多单独成一篇文章。
https://www.cnblogs.com/cheungilin/articles/15915587.html
3. C++ 特殊的关键字(待补充)
static 静态全局变量/函数,只能在本模块中使用(也就不能被extern修饰)。
const 若修饰类成员变量,则该变量为只读变量,只能在初始化列表中指定初始值,不能赋值。const成员变量并不能进入符号表成为真正的常量(一大证据就是,可以通过const_cast去除只读属性)。若修饰成员函数,则为常量函数,常量函数只能读取,不能改变数据成员,并且只能调用其他const成员函数。
extern 即告知编译器,变量/函数会在别的地方定义(一般是在其他文件中定义),这样编译器会尝试在别的文件中寻找这个变量/函数,并进行链接,而不是为本模块生成一个符号。
explicit 用来修饰类构造函数,防止实例化时产生隐式类型转换。
override 说明函数是覆盖基类函数的,让编译器检查是否实现了覆盖,而不是不小心进行了重载(类似于Java的override装饰器)。
final 将函数标明为不可被覆盖。
const 对变量是声明变量不可变,对函数则是声明不能修改成员变量。
mutable 与const相反,使得变量可以被const函数修改。
volatile 告知编译器该变量之后可能改变,需要等待其他程序修改。可以确保变量不会被优化(加载进寄存器,或者进行常量合并)、变量的读写操作也不会被优化。
noexcept,声明函数是否可能抛出有异常。例如noexcept指不会抛出异常,noexcept(false)指可能抛出异常的函数。该修饰符可以用在声明函数原型时。C++17新增此特性。补充:noexcept(expression),在编译器会计算表达式的值。C++17以前,函数默认是noexcept(true)的,除非函数中调用了会抛出异常的函数、有throw表达式、运行时可能产生转换检查等情况。
4. 保存一组bool值:vector<bool>、bitset和deque<bool>
1)vector<bool>并非真正的STL容器(或者说不是标准的STL容器)
vector<bool>被单独实现,而不是来自于vector模板。其中的元素按bit保存而不是按byte(不能很好得符合vector其他特性)。
由于C++语言层面并不能直接操作一个单独的bit,因此容器返回的类型为“proxy reference”而非“true reference”(bool类型引用)。
也就是说对于以下代码:
vector<bool> vec{false, true}; bool flag = vec[0];
给flag赋值时发生了隐式转换。vec[0]本来是“std::vector<bool>:reference”类型。
如果使用auto关键字声明的变量来获得vector<bool>中的元素,则不会发生转换。
vector<bool> vec{false, true}; auto flag = vec[0];
进一步地,若修改flag,vec[0]的值也会修改。若vec被销毁,则flag会变成类似于孤旋指针的东西。
另外 ,vector<bool>不支持一些容器应该支持的操作。例如:
void func(bool& tmp){...} vector<bool> vecc{ false, true, }; func(vec[0]); //错误,c[0]不是一个左值 bool *p = &c[0]; //错误,因为无法将一个临时量地址给绑定到指针
2)bitset和deque<bool>
如果需要保存一组bool,但不需要进行插入操作时,可以使用bitset。
如果需要进行插入等操作,则可以使用deque<bool>,它时按照STL模板标准设计的容器。
4. 继承
1)可以继承多个基类(与Java不同),但不能重复继承。比如B继承自A,那么C就不能同时继承A和B。
2)同时继承多个基类时,若基类有同名函数,子类需要进行覆盖。否则会导致继承函数冲突。子类仅需覆盖其中一个函数版本,其余版本会被隐藏掉。
5. struct和class的区别与比较
1)内部成员默认防控属性,struct默认public,而class默认private。
2)继承时默认防控属性,struct默认public,而class 默认private。注意struct和class也可以相互继承
3)class可以被用于定义模板参数,但struct不可。
4)struct支持class的大多数特性,例如不同访问控制块、友元函数、操作符重载。
C++中的struct更像是为了兼容扩展C特性而保持的,与class很相似。
6. 默认参数
与python等语言相同,可以在声明函数时,指定默认实参,默认实参需要从右向左给出。
在调用函数时,如果有指定默认实参的参数没有传入,则使用默认实参。
注意:虚函数也可以有默认实参,但默认实参值由调用时的静态类型决定。
7. Extern C
在很多标准头文件中都有类似结构:
#ifdef __cplusplus extern "C" { #endif /*...*/ #ifdef __cplusplus } #endif
其中的 __cplusplus是识别编译器的宏,当使用C++编译器时,则宏所在代码段有效。
被Extern C修饰的变量或函数应当是按照C语言的方式编译和链接,这样可以在C++中引用C语言的文件,或者使得编译出的C++代码块可以被C调用。达到C++和C语言混合编程的目的。
1)普通情况下,C++的连接
C++会将函数名称进行转换,确保在链接前所有函数拥有唯一的符号名(主要是为了应对函数重载问题)。
在链接时,会根据引用头文件去寻找对应模块对应的函数符号。
2)extern C声明后的编译和连接
即不会对函数名称进行处理,连接器寻找对应函数时,也会字节寻找未经修改的符号名(通常会加一个下划线)
8. RAII(资源获取即初始化)机制
是C++的一种管理资源和避免泄露的方法。即利用构造对象最终会被销毁的原则,将获取资源的操作封装到构造对象的构造函数中,在对象析构时会自动释放资源。
针对套接字、互斥锁、文件描述符、动态内存等。
例子:
{ public : ArrayOperation() { m_Array = new int [10]; } void InitArray() { for (int i = 0; i < 10; ++i) { *(m_Array + i) = i; } } void ShowArray() { for (int i = 0; i <10; ++i) { cout<<m_Array[i]<<endl; } } ~ArrayOperation() { cout<< "~ArrayOperation is called" <<endl; if (m_Array != NULL ) { delete[] m_Array; m_Array = NULL ; } } private : int *m_Array; }; bool OperationA(); bool OperationB(); int main() { ArrayOperation arrayOp; arrayOp.InitArray(); arrayOp.ShowArray(); return 0; }
9.C++的编译阶段
编译时会经过四步操作:预处理、编译、汇编、链接
预处理,对宏进行处理,并将包含的头文件替换为对应的内容。
编译,将经过处理的文件转换为特定的汇编代码(函数名的重写、动态模板类型确定、记录符号表等)。
汇编,将汇编代码转换为机器码。
链接,将不同编译单元间互相引用的变量/函数符号进行替换(或者跳转引用)。
浙公网安备 33010602011771号