链接:final 说明符 (C++11 起) - cppreference.com
目录
final的使用
1、不能使用 "final" 修饰符声明非虚函数
2、无法重写基类的“final”函数 "A::foo"
3、不能将“final”类类型用作基类
override 的使用 : 重写父类的虚函数
default 的使用
delete 的使用:弃置函数
两点注意:
实例:
explicit 的使用: 避免隐式转换
constexpr的使用
enum class 枚举类的使用
final的使用
1、不能使用 "final" 修饰符声明非虚函数
struct Base
{
virtual void foo();
};
struct A : public Base
{
void foo() final; // Base::foo 被覆盖而 A::foo 是最终覆盖函数
void bar() final; // 错误:bar 非虚,因此它不能是 final 的
// 不能使用 "final" 修饰符声明非虚函数
};
void A::foo()
{
}
2、无法重写基类的“final”函数 "A::foo"
struct B final : A // struct B 为 final
{
void foo() override; // 错误:foo 不能被覆盖,因为它在 A 中是 final 的
// 无法重写“final”函数 "A::foo"
};
3、不能将“final”类类型用作基类
struct C : B{}; // 错误:B 是 final 的
//不能将“final”类类型用作基类
override 的使用 : 重写父类的虚函数
C++11标准添加了一个override关键字放在派生类的虚函数后,如果编译器发现派生类重写的虚函数与基类的虚函数不一样(参数或其他不一样的地方),那么编译器将报错
放在子类重写父类的方法声明的末尾,用来检测重写的方法的正确性
通过这个功能,便于在项目中查找到对f父类虚函数进行重写时的错误。
default 的使用
声明为default的函数,编译器自动为其生成函数体
class A { public: // 声明为default的函数,编译器自动为其生成函数体 A() = default; A(const A& a) = default; A(int a){} }; int main() { A a; return 0; }
class A,没有创建无参构造方法,之所以还可以 直接 “ A a; ”创建对象, 就是因为将A() = default;//声明为default的函数,编译器自动为其生成函数体
delete 的使用:弃置函数
参考:函数声明 - cppreference.com
如果使用特殊语法
= delete;
取代函数体,那么该函数被定义为显式弃置的。任何弃置函数的使用都是非良构的(程序无法编译)。这包含调用,包括显式(以函数调用运算符)及隐式(对弃置的重载运算符、特殊成员函数、分配函数等的调用),构成指向弃置函数的指针或成员指针,甚至是在不潜在求值的表达式中使用弃置函数。但是可以隐式 ODR 使用刚好被弃置的非纯虚成员函数。
两点注意:
1、如果函数被重载,那么首先进行重载决议,且只有在选择了弃置函数时程序才非良构
2、已经声明过的函数不能声明为弃置的
实例:
// 类A 不允许拷贝,不允许赋值运算符的重载, : // thread类就是如此实现的(thread允许移动语义,即夺取某个thread对内存的控制权) class A { public: A() = default; A(const A& a) = delete; A& operator=(const A& a) =delete; // A(const A& a) // { // *this = a; // } }; int main() { A a; // A b(a); 当拷贝构造被声明为delete时,不能在被使用 return 0; }
类A的拷贝构造函数已经声明为弃置函数,无法再进行调用
explicit 的使用: 避免隐式转换
以阻止不应该允许的经过转换构造函数进行的隐式转换的发生,声明为explicit的构造函数不能在隐式转换中使用。
#include <iostream> class Test1 { public : Test1(int num):n(num){} private: int n; }; class Test2 { public : explicit Test2(int num):n(num){} private: int n; }; int main() { Test1 t1 = 12; Test2 t2(13); //Test2 t3 = 14;//错误:无法从“int”转换为“Test2” return 0; }
所以,加上explicit修饰, 指定这个构造器只能被明确的调用/使用, 不能作为类型转换操作符被隐含的使用
constexpr的使用
指定变量或函数的值可以在常量表达式中出现
1、实例1
constexpr int a = 10; //常量 // a++; //error:表达式必须是可修改的左值 constexpr int b = 3.14 * 10; //常量 std::cout<<b<<std::endl; int c = 31; switch (c) { case b: //constexpr修饰的变量,可以当作常量值使用 std::cout<<b<<std::endl; break; default: break; }
2、实例2
int x = 42; constexpr int size = x; //此时会报错:表达式必须含有常量值C/C int a[size];
3、实例3
//变量在运行时,进行赋值 int x = 20; int y = 12; // std::array<int,sum(x,y)> a1; //报错:表达式必须含有常量值 //模板在编译器时确定 //方法在运行时调用 std::array<int,sum(1,2)> a2;
enum class 枚举类的使用
优点:参考C++11枚举类——enum class-CSDN博客
1、降低命名空间污染
2、避免隐式类型转换
3、可以前置声明