constexpr
对const对比,const的值允许在运行后确定,但是constexpr不允许
const int a = get_sz();
constexpr int b = get_sz(); // 如果get_sz()不是constexpr函数,则编译出错
auto与decltype
- 普通的auto会忽略顶层const和引用
- auto&会保留顶层const,auto&a = 42(推断为int&)是错的,前面要加const得到 const int&
- decltype会保留顶层和底层const和引用
- decltype(*p)得到的是引用类型
- decltype((expr))永远都是引用类型
类型转换
数组转换
- decltype不会将数组转换为首元素的指针,故其得到的类型为数组
- sizeof也不会把数组转换为首元素的指针,故得到的为整个数组大小(字节为单位)
指针转换
- 0或字面值nullptr能转换为任意指针类型
- 任意非常量指针能转换void*
- 对象指针能转换为const void*
类类型转换
比如将字符串赋值给string,由编译器自动执行,但是不能多次
强制类型转换
static_cast<>()
常用于将void*转换成本来的指针类型(由开发者自己保证类型是正确的,否则是未定义的),或者用于基本类型之间的转换
const_cast
- 只能改变底层const(底层指的是指针指向一个常量,顶层指的是指针本身是常量)
- 不能用于类型转换,其他转换不能改变const属性
reinterpret_cast
基于更加底层的位模式转换,依赖于机器,应当尽可能避免,其无视任何限制
函数
数组形参
指针式传递
这种方式,编译器把数组当成指针
void print(const int* p);
void print(const int p[]);
void print(const int p[10]); // 10仅仅是我们期望的,编译器会忽略,均等效与const int*
引用式传递
void print(int & a[10]); // error,这代表数组是10个int 引用,这个不符合语法会编译错误
void print(int (&a)[10]); // ok, a是引用一个大小为10的数组,注意此时,10会被编译器考虑
务必小心括号问题,少括号会产生另外一种语义
void print(int * matrix[10]); // 等效于int ** matrix
void print(int (*matrix)[10]);// 指向一个含有10个元素的数组, 10会被考虑 int (*)[10]
void print(int matrix[][10]);// 与上面等效
重载和const形参
- 编译器无法区分有无顶层const,所以常量和顶层const无法同时存在
- 但底层const(指针或者引用)可以重载,编译器优先把非常量传给非const重载版本
函数指针
- 当为形参的时候,函数类型会被自动转换为函数指针
- 当作为函数指针的时候,函数类型不会被转换为函数指针,c++不允许返回函数类型
void demo(int, int);
using decltype(demo) = Func; // Func是函数类型
using decltype(demo)* = FuncPointer;// 指针类型
Func f(Func g); // error
Func* f(Func g); FuncPointer f(Func g); // ok, 形参里的Func会被转换为函数指针
类
关键字
- mutable 允许在const成员函数里面修改变量
前向声明
class A;
class B
{
A a; // error
B b; // error
A& c; // ok
A* d;// ok;
static B e; // ok
static A f; // ok
};
友元说明
注意类里面的友元说明仅仅是声明了访问权限,即便友元函数定义在类内部,也必须在外部声明,否则即便是类的成员函数也无法访问
class A
{
public:
friend void f(){cout << "f was called!\n"};
void gg(){f();} // error, f还未被声明
void hh();
};
void f();
void A::hh(){f();} // ok
静态类成员
- 必须在外部声明类静态成员变量,否则无法使用,类外声明是无需再写static关键字,但数据类型要写如 int A::w = 10;
- 若想在类内初始化类内静态变量,必须将其声明为常量const/constexpr
- 对于在类内提供初值的常量静态成员变量,我们也应该在类外声明一下(但不要提供初值)
delete声明符作用与析构函数
此时只能使用new创建对象,且无法delete释放
构造/析构函数调用虚函数
- 可以理解为虚机制失效,由构造/析构函数所属类确定
- 因为基类构造先于派生类构造,派生类析构先于基类构造执行
使用using继承构造函数
using可以指示编译器“继承”基类构造函数,这里不是真正的基础,而有编译器自动生成一些构造函数,这些构造函数与基类构造函数有相同的参数列表,如下
derived(pargram):base(pargram)
-
using不会继承默认、拷贝、移动构造函数
-
对于含有默认参数的基类构造函数,则会生成多个构造函数,每个构造函数形参数量从最多,依次递减到非默认参数数量
如基类构造函数有两个参数,一个默认,则会生成接受两个参数的和接受左边那个参数的两个构造函数