final/override/=default/=delete 语法

📅 2026/7/2 11:18:18
final/override/=default/=delete 语法
final、override、default、delete是C11添加的一组非常具有标记意义的新语法我们来逐一介绍它们。final 关键字final关键字修饰一个类这个类将不允许被继承这在其他语言如Java中早就实现了。在C 11中final关键字要写在类名的后面其他语言是写在class关键字的前面。示例如下class A final { }; class B : A { };由于类 A 被声明成finalB 继承 A 编译器会报如下错误提示 A 不能被继承error C3246: B : cannot inherit from A as it has been declared as finaloverride 关键字C 语法规定父类中加了virtual关键字的方法可以被子类重写子类重写该方法时可以加或不加virtual关键字例如像下面这样class A { protected: virtual void func(int a, int b) { } }; class B : A { protected: virtual void func(int a, int b) { } }; class C : B { protected: void func(int a, int b) { } };这种宽松的规定可能会带来两个问题当我们阅读代码时无论子类重写的方法是否添加了virtual关键字我们都没法直观地确定该方法是否是重写的父类方法如果我们在子类中不小心写错了需要重写的方法的函数签名可能是参数类型、个数或返回值类型这个方法就会变成一个独立的方法这可能会违背我们最初想重写父类某个方法的初衷而编译器在编译时并不会检查到这个错误。为了解决以上两个问题 C 11 引进了override关键字其实override关键字并不是什么新语法在Java等其他语言中早就支持。被override修饰的类方法是改写父类的同名方法加了该关键字后在编译阶段编译器会作相应的检查如果其父类不存在相同签名格式的类方法编译器会给出相应的错误提示。情形一父类没有子类标记了override的方法class A { }; class B : A { protected: void func(int k, int d) override { } };由于父类 A 中没有 func 方法编译器会提示错误error C3668: B::func : method with override specifier override did not override any base class methods情形二父类有子类标记了override的方法但函数签名不一致class A { protected: virtual int func(int k, int d) { return 0; } }; class B : A { protected: virtual void func(int k, int d) override { } };编译器会报同样的错误。正确代码class A { protected: virtual void func(int k, int d) { } }; class B : A { protected: virtual void func(int k, int d) override { } };default 语法如果一个 C 类没有显式地给出构造函数、析构函数、拷贝构造函数、operator 这几类函数的实现在需要它们时编译器会自动生成或者在给出这些函数的声明时如果没有给出其实现编译器在链接时就会报错。default如果标记这类函数编译器会给出默认实现。我们来看一个例子class A { }; int main() { A a; return 0; }这样的代码是可以编译通过的因为编译器会默认生成一个 A 的无参构造函数假设我们现在给 A 提供一个有参数形式的构造函数class A { public: A(int i) { } }; int main() { A a; return 0; }这个时候编译器就不会自动生成默认无参数的构造函数了这段代码会编译出错错误提示 A 没有合适的无参构造函数error C2512: A : no appropriate default constructor available我们这个时候可以手动给 A 加上无参构造函数也可以使用default语法强行让编译器自己生成class A { public: A() default; A(int i) { } }; int main() { A a; return 0; }default笔者觉得最大的作用就是在开发中简化了那些构造函数中没有实际的初始化代码的写法尤其是声明和实现分别属于一个 *.h 和 *.cpp 文件。例如对于类 A其头文件为a.h其实现文件为a.cpp正常情况下我们需要在a.cpp文件中写其构造函数和析构函数的实现可能没有实际构造和析构代码//a.h class A { public: A(); ~A(); };//a.cpp #include a.h A::A() { } A::~A() { }a.cpp中构造函数和析构函数我们不得不写上有了default关键字我们可以在a.h中直接写成//a.h class A { public: A() default; ~A() default ; };//a.cpp #include a.h //这里不用在写A的构造函数和析构函数的实现了delete 语法既然有强制让编译器生成构造函数、析构函数、拷贝构造函数、operator 的语法那么也应该有禁止编译器生成这些函数的语法没错就是delete。函数在 C 98/03 规范中如果我们想让一个类不能被拷贝即不能调用其拷贝构造函数我们可以将其拷贝构造和 operator 函数定义成 private 的。class A { public: A() default; ~A() default; private: A(const A a) { } A operator (const A a) { } }; int main() { A a1; A a2(a1); A a3; a3 a1; return 0; }以上代码在利用 a1 构造 a2 时编译器会提示错误error C2248: A::A : cannot access private member declared in class A error C2248: A::operator : cannot access private member declared in class A我们利用了这种方式间接实现了一个类不能被拷贝的功能这也是继承自boost::noncopyable的类不能被拷贝的实现原理。现在有了delete语法我们直接使用该语法直接禁止编译器生成这两个函数即可class A { public: A() default; ~A() default; public: A(const A a) delete; A operator (const A a) delete; }; int main() { A a1; //A a2(a1); A a3; //a3 a1; return 0; }一般在一些工具类中我们不需要用到构造函数、析构函数、拷贝构造函数、operator 这四个函数为了防止编译器自己生成同时也是为了减小生成的可执行文件的体积笔者建议使用delete语法将这四个函数“删除”例如class EncodeUtil { public: static std::wstring EncodeUtil::AnsiToUnicode(const std::string strAnsi); static std::string UnicodeToAnsi(const std::wstring strUnicode); static std::string AnsiToUtf8(const std::string strAnsi); static std::string Utf8ToAnsi(const std::string strUtf8); static std::string UnicodeToUtf8(const std::wstring strUnicode); static std::wstring Utf8ToUnicode(const std::string strUtf8); private: EncodeUtil() delete; ~EncodeUtil() delete; EncodeUtil(const EncodeUtil rhs) delete; EncodeUtil operator(const EncodeUtil rhs) delete; };