现代 C++ 编程实战
吴咏炜
前 Intel 资深软件架构师
34196 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 51 讲
加餐 (1讲)
现代 C++ 编程实战
15
15
1.0x
00:00/00:00
登录|注册

09 | 易用性改进 II:字面量、静态断言和成员函数说明符

cppreference.com, “非静态成员函数”, “特殊成员函数”部分
cppreference.com, “Non-static member functions”, section “Special member functions”
cppreference.com, “用户定义字面量”
cppreference.com, “User-defined literals”
维基百科, “火星气候探测者号”
Wikipedia, “Mars Climate Orbiter”
用法示意
final的作用
override的作用
C++11引入
示例
改变默认行为
不同状态的特殊成员函数
默认特殊成员函数的生成规则
例子展示
static_assert语法
C++11引入
实际例子
不同情况下的分隔符约定
为数字型字面量添加 ' 以提高可读性
C++14引入
I/O streams中的不足
在需要比特级操作时有用
0b前缀
C++14引入
在自定义类中支持字面量
使用命名空间
例子展示
标准库中的标准字面量
operator"" 后缀
C++11引入
参考资料
课后思考
内容小结
override 和 final 说明符
default 和 delete 成员函数
静态断言
数字分隔符
二进制字面量
自定义字面量
易用性改进 II:字面量、静态断言和成员函数说明符

该思维导图由 AI 生成,仅供参考

你好,我是吴咏炜。
本讲我们继续易用性的话题,看看现代 C++ 带来的其他易用性改进。

自定义字面量

字面量(literal)是指在源代码中写出的固定常量,它们在 C++98 里只能是原生类型,如:
"hello",字符串字面量,类型是 const char[6]
1,整数字面量,类型是 int
0.0,浮点数字面量,类型是 double
3.14f,浮点数字面量,类型是 float
123456789ul,无符号长整数字面量,类型是 unsigned long
C++11 引入了自定义字面量,可以使用 operator"" 后缀 来将用户提供的字面量转换成实际的类型。C++14 则在标准库中加入了不少标准字面量。下面这个程序展示了它们的用法:
#include <chrono>
#include <complex>
#include <iostream>
#include <string>
#include <thread>
using namespace std;
int main()
{
cout << "i * i = " << 1i * 1i
<< endl;
cout << "Waiting for 500ms"
<< endl;
this_thread::sleep_for(500ms);
cout << "Hello world"s.substr(0, 5)
<< endl;
}
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

C++11和C++14引入了许多易用性改进,包括自定义字面量、二进制字面量、数字分隔符和静态断言。自定义字面量允许使用`operator""`后缀将用户提供的字面量转换成实际的类型,而二进制字面量则使得直接表示二进制数成为可能。此外,数字分隔符允许在数字型字面量中任意添加`'`以提高可读性,而静态断言机制则允许在编译时检查假设是否成立。这些改进使得C++在易用性方面有了显著提升,为开发人员提供了更加便捷和友好的编程体验。 另外,C++11引入了`default`和`delete`成员函数,允许开发人员改变默认行为,删除编译器默认提供的特殊成员函数,或者明确声明需要默认提供的特殊成员函数。这些改进使得代码更加清晰,减少了潜在的错误。 此外,`override`和`final`说明符的引入也为C++11带来了易用性改进。`override`用于显式声明成员函数覆盖了基类中的虚函数,而`final`则声明了成员函数不可在派生类中被覆盖,或者标志某个类或结构不可被派生。这些新特性可以提供更明确的提示和额外的检查,防止程序员由于拼写错误或代码改动没有让基类和派生类中的成员函数名称完全一致。 总的来说,这些易用性改进使得C++在代码可读性、错误预防等方面有了显著提升,为开发人员提供了更加便捷和友好的编程体验。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《现代 C++ 编程实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(23)

  • 最新
  • 精选
  • 木瓜777
    您好,您有没有感觉比较好的开源c++项目推荐? 希望从别人的项目中学到一些经验,谢谢!

    作者回复: C++ 项目入门都不容易,要找自己有兴趣的领域是关键。除了第 6 讲评论里推荐的那些,可以考虑下面两个(我将来也会讲到): - EasyLogging++ - Catch2

    2019-12-17
    2
    13
  • 可爱的小奶狗
    代码如下: #include <iostream> #include <memory> using namespace std; class dummy{ }; class Boy{ public: Boy(){ cout<<"boy constructor"<<endl; } Boy(Boy& m){ cout<<"boy copy constructor"<<endl; } Boy(Boy&& m){ cout<<"boy move constructor"<<endl; } }; class Man{ public: Man(){ cout<<"man constructor "<<endl; } Man(Boy boy){ cout<<"man constructor with boy "<<endl; } Man(Man &m){ cout<<"copy constructor"<<endl; } Man(Man&& m){ cout<<"move constructor"<<endl; } private: Boy a; }; int main(){ dummy d; dummy d2; d = d2; dummy d3 = d2; std::cout << "***************" <<std::endl; Boy&& boy = Boy(); Boy& b = boy; std::cout << "/////////////" <<std::endl; make_shared<Man>(boy); std::cout << "###############" <<std::endl; make_shared<Man>(move(boy)); } 输出的结果: *************** boy constructor ///////////// boy copy constructor boy constructor 疑惑:为什么会调用Boy的构造函数呢? man constructor with boy ############### boy move constructor boy constructor man constructor with boy Process finished with exit code 0 老师,如上“疑惑”所述,为什么会调用Boy的构造函数呢?我觉得不应该调用Boy的构造函数,因为调用了boy 拷贝构造,然后再调用Man的拷贝构造,就可以了。想不通。

    作者回复: 你在 Man 类里有私有成员变量 Boy a,又没有去初始化,那不就默认构造了么。 如果你想用参数 boy 去拷贝构造 a,你需要写: Man(Boy boy) : a(boy) { cout << "man constructor with boy " << endl; } 这样会拷贝构造两次。我想你真正需要的可能是(我加上了拷贝构造函数的 const): class Boy { public: Boy() { cout << "boy constructor" << endl; } Boy(const Boy& m) { cout << "boy copy constructor" << endl; } Boy(Boy&& m) { cout << "boy move constructor" << endl; } }; class Man { public: Man() { cout << "man constructor " << endl; } Man(const Boy& boy) : a(boy) { cout << "man constructor with boy " << endl; } Man(Man& m) { cout << "copy constructor" << endl; } Man(Man&& m) { cout << "move constructor" << endl; } private: Boy a; };

    2020-09-15
    4
  • 三味
    如果对一个函数声明了final, 我觉得没有必要在添加override了吧. override就是明确声明这是一个继承来的函数, final同样也是这个意思, 只不过final更霸道, 后续的不要在继承了! 如果从一开始就不想让别的函数去继承而写final, 那就根本没必要去virtual它. 何必要在虚函数表中添加一个没有继承作用的虚函数呢? PS: 数字分隔符和自定义字面量真是学到了. 在9102年的尾巴, 我才知道有这么邪道的用法... PPS: 前些日子看了一些国外游戏大牛各种喷C++的帖子. 本章的所有内容应该都是他们喷的范围吧... 那些人特别看中编译时间, 追求极致的运行效率... 有个人专门对比了int a=7; 和 int a{7}的性能差别...从编译到运行时间... 利用宏展开的方式, 对这两个例子分别做了百万次展开, 如果用vs测试都能爆IDE内存的级别... 我觉得对于中小型对性能不是特别敏感的程序, 这些还是很有用的. PPPS: 最近对Data-oriented design感兴趣, 不知道作者以后是否有开这类理论实战课程的计划捏? 我搜索上面喷神, 就是从这里开始搜索出来的...

    作者回复: 对,final override 合法但不必要。 当然不是所有内容都是被那个游戏开发人士喷的。他抱怨的是会导致编译速度下降,以及非优化编译性能差的那部份功能。通常都是模板相关的。所以这一讲的内容不属于其中。

    2019-12-20
    4
  • zhengfan
    吴老师,您好。 请问您在static_assert中使用的例子,是不是应该判断alignment && (alignment & (alignment - 1) ==0)? 另外,这个算法真是精巧👍。效率之高让我甚至一度怀疑命题的等价性。证明了一下确实是等价的。

    作者回复: 0 也是 2 的整数次幂,没毛病。😁 这是很标准的算法,我很早就看到过。 从实用的角度,这一个判断可能确实不够。我的实际代码里有: STATIC_ASSERT(alignment::value > 0 && alignment::value <= 8192, Bad_alignment); STATIC_ASSERT((alignment::value & (alignment::value - 1)) == 0, Alignment_must_be_power_of_two);

    2020-07-01
    3
    3
  • 王旧业
    length的例子里,字面量operator""实现中参数是long double,但是length成员变量value使用的是double,这是有意为之吗?

    作者回复: 哦,这是因为 operator"" 的参数要求是 long double(不是 double)。而一般的类里,double 的精度就够用了。倒不算什么刻意为之吧。

    2020-03-15
    2
  • 瓜农
    一直有种感觉,相对于java,c++的语法设计略显随意不够自洽。 譬如const/final在java里面就final搞定了,vitual这个关键字也感觉比较鸡肋。 老师怎么看

    作者回复: 如果你不需要性能,那你可以有抽象和优雅。见 Python。 如果你不需要抽象,那你可以有简单和性能(但程序员用起来会很费力)。见 C。 如果你不需要向后兼容性,用几十年的经验设计一种全新的语言,那你也至少可以做到兼顾抽象、优雅和性能(简单是简单不了了)。见 Rust。 如果你啥都想要:性能,抽象,向后兼容性。那你就忘掉优雅吧,你会得到一个类似于 C++ 的东西。除非有人在 1980 之前能开天眼,那也许能设计出一个更优雅的东西。 但那也不会是 Rust——在 C++ 诞生的年代,Rust 的编译速度恐怕没有任何人能承担得起。 也不会是 Java。虚函数是有性能代价的,甚至是巨大的性能代价。Java 的虚拟机都是要用到 C++ 的。 另外,const 为什么要用 final 来修饰?我觉得 Java 的这个设计也很没道理。C++ 里 final 基本不需要,也是受了其他语言的影响才有的,并且是个说明符,不是关键字。我在项目里从来没写过 final(override 还是经常需要的)。

    2022-06-04
    1
  • 常振华
    唯一喜欢的易用性改造就是列表初始化,其它的反而增加了复杂度。

    作者回复: 这也只是你的一家之言。大部分人最喜欢的是 auto。

    2021-09-30
    1
  • geek
    老师,关于子类中重新定义虚函数这个动作我之前认为是重写(overwrite),但也碰到有人说是重载(overload),另外我看文章中说的是覆写(就是override的翻译吧?)。 我个人倾向于overwrite或者override,因为重新定义虚函数这个动作涉及了多个类,而重载是指同一个类中的一组同名但形参个数或者形参类型不同的方法。我认为override是包含overwrite的,overwrite只说子类重新实现基类虚函数这种情况,而override则是覆盖,只要子类有了和基类中同名的方法,都会隐藏父类中的同名方法,如果子类能创建对象,必须要实现虚函数,因而就会隐藏基类中的同名方法。 老师看下我说的有问题吗?

    作者回复: 对。override 的函数的参数是一样的,子类覆盖父类。overload 的函数的参数是不一样的,根据参数来选择合适的函数。两者不能混淆。

    2021-03-05
    1
  • Milittle
    之前用default delete override final较多 static_assert 和 自定义字面量和二进制字面量用的少 争取以后可以使用在自己的项目中 提供便利 老师的硬货很多 受益匪浅 感谢

    作者回复: 小改进,适合的就用起来。但也不用强求为了用而用。

    2020-02-25
    1
  • 小一日一
    我最喜欢的C++易用性改进及理由: auto: 少打字 scope for:少打字 类成员默认补始化语法:少打字 default 和 delete 成员函数:简化对类行为的控制难度 自定义字面量:代码看起来舒服。

    作者回复: 都用上了吗?很好啊。

    2019-12-16
    2
    1
收起评论
显示
设置
留言
23
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部