=default,=delete
default显示声明该函数自动生成函数体,即默认函数,仅适用于类的特殊成员函数
delete 禁用该函数,比如若不想使用默认的拷贝函数或赋值函数,可以使用delete
一旦基类中使用=delete 来声明基类的某个成员函数为删除的,那么派生类中的对应函数也会自动的变成删除的
就可以用delete来禁用函数而不是private
因为就可以保证在同一个类也不能调用这些方法 如果用private 就会在链接的时候报错
如果要把private的改成= delete的 就要加个delete 还要从private里挪 public 里
委托构造函数
在一个构造函数中调用另外一个构造函数,不同的构造函数自己负责处理自己不同的情况,把最基本的构造工作委托给某个基础构造函数完成,实现分工操作
主要是因为老C++中发现每实现一个构造函数,大部分代码都是相同的
class A
{
A(int _a,int _b,int _c):a(_a),b(_b),c(_c) {}
A() :A(0, 0, 0) {}
A(int _a) :A(_a, 0, 0) {}
private:
int a;
int b;
int c;
};
constexpr构造函数
constexpr变量:编译器会验证该变量的值是否是一个常量表达式。声明为constexpr的变量一定是常量,而且必须用常量表达式初始化
constexpr int a = 10;
constexpr int b = a+1;//OK
constexpr int c = GetVal();//只有GetVal()是一个constexpr函数时正确
const 和constexpr的主要区别:const变量的初始化可以延迟的运行时,而constexpr必须在编译时初始化,const承诺不改变这个值,主要用于说明接口,这样变量传递给函数就不担心变量会在函数内被修改了,编译器负责。constexpr在编译时求值,主要用于说明常量,允许数据置于只读内存以及提升性能。const并未区分出编译期常量和运行期常量,constexpr限定了编译期常量
constexpr函数:在编译时计算其返回值的函数,当其参数为constexpr值并且在编译时使用代码需要返回值时(如初始化一个constexpr变量)它会生成编译时常量,使用非constexpr参数调用时,或编译时不需要其值时,他将与正则函数一样,在运行时生成一个值
字面值:只能用它的值来称呼它
字面值常量类:constexpr函数的参数和返回值都必须为常量表达式,而常量表达式的最基本要素就是字面值类型,某些类也属于字面值类型,C++称之为字面值常量类主要就是包括两种情况
- 数据成员都是字面类型的聚合类就是一种,字面值的聚合继续具有字面值的特征
聚合类:
所有成员都是public
没有定义构造函数
没有类内初始值
没有基类,也没虚函数 - 另一个情况:数据成员都必须是字面值类型。类必须至少带一个constexpr构造函数,如果一个数据成员类内有类内初始值,则初始化必须是常量表达式,如果成员属于某种类,初始值必须使用该类的constexpr构造函数,类必须使用析构函数默认定义
constexpr构造函数:通过前置constexpr关键字,就可以声明constexpr构造函数,constexpr构造函数的函数体一般为空,使用初始化列表或者其他的constexpr构造函数初始化所有数据成员
struct A {
constexpr A(int _x, int _y) :x(_x), y(_y) {}
constexpr A() : A(0, 0) {}
int x;
int y;
};
constexpr构造函数可在使用constexpr表达式或者constexpr函数的地方使用常量类了,编译期完成计算。
POD(Plain Old Data)
POD:一个类或结构体通过二进制拷贝(memcpy)后还能保持不变,POD类型在源代码兼容ANSI C 时非常重要。POD对象与C语言的对应对象具有共同的一些特性,包括初始化、复制、内存布局、寻址等、当一个数据类型满足了“平凡定义”和“标椎布局”,我们则认为调试一个POD数据
平凡的定义:(没有自己去定义,使用默认的)
- 有平凡的构造函数
- 有平凡的拷贝构造函数
- 有平凡的移动构造函数
- 有平凡的拷贝复制运算符
- 有平凡的移动赋值运算符
- 有平凡的析构函数
- 不能包含虚函数
- 不能包含徐基类
C++也提供了一个方法来判断类是不是平凡的:
std::is_trivial<A>::value
Old:
标准布局定义:
- 所有非静态成员有相同的访问权限
- 派生类中有非静态的数据 那么它的基类只有是静态的。如果基类有非静态的,那么派生类不能有非静态的
- 派生类的非静态成员不可以是基类类型
- 没有虚函数
- 没有虚基类
- 所有非静态成员都符号标准布局类型
可以用 std::is_standard_layout<A>::value来判断是否为标准布局的
当一个数据类型满足“平凡的定义”和“标准布局”我们认为他是个POD数据。
可以通过std::is_pod<A>来判断一个类型是否为POD
explicit构造函数
explicit关键字的作用就是防止类构造函数的隐式自动转换
explicit关键字只能用于类内部的构造函数声明上,而不能用在类外部的后函数定义上。它的作用是禁止隐式转换
explicit 作用于单个参数的构造函数,如果构造函数有多个参数,单数从第二个参数开始,如果各参数均有默认值,也可以应用explicit关键字,当不希望进行自动类型转换时用explicit,标准库许多构造函数都是explicit
final关键字
final作用于虚函数
放在函数后边,表示该函数已经处于最终状态,后面的派生类不能重写这个虚函数
final通常只在继承关系的中间终止派生类中虚函数的重写中有意义 如果把基类中的虚函数加上final关键字,将使函数无法被重写,这样也使得该函数无法被重写,就失去了虚函数的意义,万能墙可以直接将该函数定义为非虚函数
final作用在类,表示此类不能被继承
class A final{};//此类不能被继承
class B:public A{};//error
override
C++中对于基类声明为虚函数,之后重写都不用声明为虚函数,就容易被忽略,使得程序员很难看出这是虚函数
override:派生类在虚函数声明时使用此关键字,那么该函数必须重写其基类中的同名函数,否则代码无法通过编译
继承构造函数 - 继承中构造函数的透传
B派生与A B又在构造函数中调用A的构造函数,从而完成了构造函数的“传递”或叫透传。
class A {
public:
A(int i): val(i) {}
int val;
};
class B :public A
{
public:
B(int i, int j) :A(i), d(j) {}
int d;
};
继承构造
class A {
public:
A(int i): val(i) {}
A(char c) :ch(c) {}
private:
char ch;
int val;
};
class B :public A
{
public:
using A::A;
private:
int d{ 0 };
};