C++ 类使用注意事项

前言 本文包含一些在使用 C++ 类时的注意事项,可避免一些常见问题,并能让你在写代码时更加自信。 因为目前我使用的是 C++ 17,所以仅保证以下内容在 C++ 17 中正确。 构造函数 对于单个参数的构造函数,推荐添加 explicit 关键字,防止隐式转换错误调用构造函数。 class DemoClass { explicit DemoClass(int test) { this->test_ = test; } } 显式声明有参构造函数后,编译器不会自动生成无参构造函数,若需要,请添加 DemoClass() = default; class DemoClass { explicit DemoClass(int test) { this->test_ = test; } DemoClass() = default; } 若子类中没有显式调用父类的构造函数,子类会默认调用父类的无参构造函数,如果父类没有无参构造函数,会报错。 析构函数 基类析构函数应该声明为 virtual,这样可以防止子类无法正确的析构。 由于基类析构函数为 virtual,派生类的析构函数应该显式的 override。 class DemoClass { virtual ~DemoClass() = default; } class ChildClass: public DemoClass { ~ChildClass() override { xxx; } } 成员变量默认值 对于类或局部作用域中未明确指定默认值的成员变量,其值遵循以下规则: 对于原生类型(primitive types),即 int、float、int*、string* 等,默认值为随机的脏数据。 对于对象(objects),例如自定义类、或 std::string 等,会调用默认(无参)构造函数,若没有默认(无参)构造函数,则报错。 对于引用(reference)类型,例如 std::string&,必须赋初始值或在构造函数中初始化,若为初始化,则报错。 float age; // 脏数据、随机值(无意义),此时访问会出现未定义行为 int *ptr; // 脏数据、随机值(无意义),此时访问会出现未定义行为 string name; // 调用默认构造函数,对于 std::string 来说为空字符串 "" DemoClass demo; // 若 DemoClass 存在无参构造函数,则调用,否则编译错误 string *pname; // 脏数据、随机值(无意义),此时访问会出现未定义行为 string &rname; // 编译错误,必须显式初始化 const string &crname; // 同上,编译错误 成员变量初始化方式 初始化成员变量一般而言有四种方式,一般而言第二种和第四种方式为等价的,不过在下面我们会看到一种例外: ...

November 9, 2022 · zzsqwq

关于美的一些思考

楔子 昨天听了学弟们关于博客搭建的一些教程,在听的过程中,也想起了我之前对于博客的种种尝试,发现整体的趋势就是,简约再简约。 这引发了我对我过去几年的审视,感觉自己的审美一直在变化,对于美的认识和感受也是一直在变的,故写此文来记一些我对美认识的思考。 简约是我最终的归宿 遥记得我还在小学和初中时,非常迷恋 QQ飞车 这款游戏,这款游戏里面有一个虚拟人物,人物可以搭配各种装饰,包括但不限于上衣、裤子、发型、发饰、翅膀等,起初我是喜欢要多花哨有多花哨造型,各种发饰、翅膀、脸部特效搞的飞起,现在想起来真是非常搞笑,而且喜欢五颜六色的,当时觉得,真的是太好看了。 但是过了一段时间,就开始感觉不对劲了,这么花哨,这么丑,这怎么可能是我当时亲手选择的造型。后来就逐渐向简约范靠拢,装饰越简单,色调越单一越喜欢。 下面的几张图大致可以窥见这个过程: 而长大后,对于 Blog 的搭建貌似也是这个过程。一开始喜欢相对花哨的,包括各种炫酷的背景、动效等,因此一开始选择了高定制度、有各种花哨特效的 Hexo 框架,不过当时已经在飞车中喜欢上了简约风格,因此主题也选择了 Hexo 中相对简洁的 NexT 主题。 后来发现 Hexo 实在操作过程有些过于繁杂,从操作的意义上来说,他无法做到足够的简洁、简约和高效。 故而我选择了操作更加方便(例如发表博文、操作主题元素)更加方便的动态博客——Typecho。 起初,我选择延续了在 Hexo 上的 NexT 主题,后来发现了更加符合我审美的 Handsome 主题,第一眼就是非常喜欢,后来忍痛买了主题,用了一段时间后,又逐渐觉得主题没有那么好看,有些看腻了,而且动态博客的升级、安装插件,也是十分繁杂。 最后,偶然之下,发现了现在在使用的 Hugo 框架,它生成迅速、无需升级、也有着很多简约风的主题,完美符合我的需求,果断迁移了过来,具体流程可见:记一次博客迁移记录 - Zs’s Blog ,一直用到现在,还是对这个框架和主题算是相对满意的。 总体而言,无论是操作还是页面,都是越来越简约、统一了。不知道未来,我会不会觉得这个也不够简约和统一,再去找寻其他的框架呢。 各种产品的变迁 除了上面我自己对美认识的变化,我觉得整个社会对美的认识也是存在变化的。 往大了讲,是各种建筑、各种设施外观的变化。 往小了讲,我们用的手机、手机上的软件(例如 QQ、微信),他们的外观每年都在迭代,可能每次变化时有人吐槽有人叫好,但是整体而言,至少对我来说,他是整体都是越来越好看的,虽然逐渐变得臃肿,但是 UI 确实是“实打实”的在进步。下图是 QQ 的变化(左侧是网友仿的一个老版 QQ 界面1,右图是最新版 Mac QQ),可见一二: 那就引发了我一个思考,大家对于美的认识是被塑造了呢,还是说这个东西就是客观上的变好看了,美毕竟是一个比较主观的东西。 如果他是主观上变好看了,那么他是真的好看了吗?而且为什么可以做到如此的统一,让大多数人都觉得变好看了。 如果他是客观上变好看了,那么到底是什么因素的存在让人觉得他好看呢? 我觉得美这个东西,很不像生活中常见的发展规律,例如芯片的研发,需要数学、物理、化学等学科实际理论的支持。 2001 年的人是没有办法设计出 Apple M2 的芯片的,因为受到了各种硬件的制约。而 2001 年的设计师有没有可能设计出最新版 Mac QQ 这样的界面呢,如果不能,是什么制约着它呢?如果能,为什么没有出现这种设计,是因为当时大家觉得他不好看吗? 因为我不是学设计和美术等专业的,并且这种很很直觉的东西我不知道该怎么去搜索,因此想在这里和大家探讨一下。如果有已经相对成熟的理论,还请大家能够告知与我,解答困扰我好久的疑问。 AI 绘图 看网上有人调侃说,愿意称 2022 年为 AI 绘图元年。 ...

October 30, 2022 · zzsqwq

如何善用搜索引擎?

前言 我们使用搜索引擎可能是为了搜集信息、或者是解决一些问题。但如果不能正确的使用搜索引擎,使用关键字等,可能搜到的答案就与问题相关不大,或者与我们的问题相差甚远。 这篇文章意在分享我个人在学习、工作中使用搜索引擎的一些技巧与思考,可能后续会不断的更新,如有纰漏,还请大家指正。 应该使用什么搜索平台? 想要提高搜索的效率,选择一个好的搜索引擎或者内容平台颇为重要。 下面列举的是我平时会使用的一些平台,列出的顺序代表了我个人对于他们的推荐程度: 通用搜索引擎:Google、Bing、DuckDuckGo、Baidu 编程相关问题:Stack Overflow、Github Issue、segmentfault、稀土掘金、腾讯云开发者社区、CSDN、华为云开发者社区 日常问题:V2EX 第一类为通用搜索引擎,例如 为什么最近头发掉的更多了? 在其上可以搜索任何问题,其中 Google 的使用门槛相对较高,而 Bing 针对国内用户来说是一个相对不错的选择,不建议常用 Baidu,因为其上广告非常多,有用信息密度相对较小。 第二类为编程相关问题,例如 CMake 为什么报错了? 其中 Stack Overflow、Github 虽均为国外的平台,但是国内是可以访问的,不过在 Baidu 搜索引擎中词条相对靠后,同时也需要使用英文来进行问题检索。(Btw,Stack 系列还有例如 Stack Exchange 平台,它的模式与前者相同,但是更偏日常一些。)除此以外,后面的平台均为国内平台,所列是我平时常见的一些,整体而言,前面的文章质量普遍会比后面的高一些,其中腾讯云开发者社区、CSDN、华为云开发者社区三者内容重叠度较高。 第三类为日常问题,例如 Mac 上有什么好用的 App ?可以在 V2EX 上寻找答案,当然,它也是包罗万象的,只不过我常用它来解决日常各类小问题。 如何正确的知道问题所在? 我们遇到一个问题,要首先能够定位这个问题的关键。 例如编译中出错了,可能有很多 log,你必须能够精确定位到到底哪一条 log 才是这次编译出错的关键,进而才能解决这个问题。 以 CMake 为例,编译时报错 log 一般为红色,警告 log 一般为黄色,普通 log 一般为白色。得益于这种等级/颜色分明的 log ,我们可以快速的定位报错。我们日常编程中,为了便于 Debug,最好也遵循这个规范,例如在 C++ 中打印错误 log 时推荐使用 std::stderr 而不是 std::stdout 。 见过很多身边的同学遇到了报错,就一股脑粘贴所有的报错 log,然后贴到搜索引擎中,发现相关的错误基本没有。这就是没有其中错误的关键,报错时的 log 可能 100 行只有 10 行是关键的,甚至只有一行是关键的,例如 100 行 log 中只有 未定义的引用(undefined reference to ‘xxx’) 这一句是最关键的。 ...

October 30, 2022 · zzsqwq

为什么要使用条件变量?

为什么要使用条件变量? 前言 最近看了很多与线程有关的 C++ 新特性,条件变量是见的比较多的一个特性。 看的时候我发现,想要理解一个新的特性,关键的要看它的引入到底解决了哪些问题,没有什么特性我们要实现相同的功能要怎么做? 以我的理解来看,条件变量是一个线程间互相同步与通知的手段,他通过主动唤醒的方式减小了各个线程的开销,取代了简单但是消耗较大的一直被动循环检验与等待。 没有条件变量我们如何实现相同的需求? 这里采用现代C++教程1 中关于条件变量的一个例子作为基础: 不使用条件变量版本 #include <queue> #include <chrono> #include <mutex> #include <thread> #include <iostream> #include <condition_variable> int main() { std::queue<int> produced_nums; std::mutex mtx; // 生产者 auto producer = [&]() { for (int i = 0; ; i++) { std::this_thread::sleep_for(std::chrono::milliseconds(900)); std::unique_lock<std::mutex> lock(mtx); std::cout << "producing " << i << std::endl; produced_nums.push(i); } }; // 消费者 auto consumer = [&]() { while (true) { { std::unique_lock<std::mutex> lock(mtx); if(produced_nums.empty()) continue; } std::unique_lock<std::mutex> lock(mtx); // 短暂取消锁,使得生产者有机会在消费者消费空前继续生产 lock.unlock(); // 消费者慢于生产者 std::this_thread::sleep_for(std::chrono::milliseconds(1000)); lock.lock(); while (!produced_nums.empty()) { std::cout << "consuming " << produced_nums.front() << std::endl; produced_nums.pop(); } } }; // 分别在不同的线程中运行 std::thread p(producer); std::thread cs[2]; for (int i = 0; i < 2; ++i) { cs[i] = std::thread(consumer); } p.join(); for (int i = 0; i < 2; ++i) { cs[i].join(); } return 0; } 使用条件变量版本 #include <queue> #include <chrono> #include <mutex> #include <thread> #include <iostream> #include <condition_variable> int main() { std::queue<int> produced_nums; std::mutex mtx; std::condition_variable cv; bool notified = false; // 通知信号 // 生产者 auto producer = [&]() { for (int i = 0; ; i++) { std::this_thread::sleep_for(std::chrono::milliseconds(900)); std::unique_lock<std::mutex> lock(mtx); std::cout << "producing " << i << std::endl; produced_nums.push(i); notified = true; cv.notify_all(); // 此处也可以使用 notify_one } }; // 消费者 auto consumer = [&]() { while (true) { std::unique_lock<std::mutex> lock(mtx); while (!notified) { // 避免虚假唤醒 cv.wait(lock); } // 短暂取消锁,使得生产者有机会在消费者消费空前继续生产 lock.unlock(); // 消费者慢于生产者 std::this_thread::sleep_for(std::chrono::milliseconds(1000)); lock.lock(); while (!produced_nums.empty()) { std::cout << "consuming " << produced_nums.front() << std::endl; produced_nums.pop(); } notified = false; } }; // 分别在不同的线程中运行 std::thread p(producer); std::thread cs[2]; for (int i = 0; i < 2; ++i) { cs[i] = std::thread(consumer); } p.join(); for (int i = 0; i < 2; ++i) { cs[i].join(); } return 0; } 这两段代码在效果上是等效的,都是一个生产者两个消费者。 ...

August 24, 2022 · zzsqwq

一个基于 Hugo 的个人主页主题

背景 之前看到过学长学姐做过个人主页,多是用来申请一些学校的夏令营使用的,觉得非常的 Nice,自己也想搞一个。 大家之前好像大多用的是基于 Hexo 的一个主题 — hexo-theme-academia,但是由于之前我用过 Hexo,觉得他有一些比较明显的弊端,例如环境配置比较麻烦、需要安装 Nodejs、npm 等环境,然后再安装 Hexo。其次它的构建速度比较慢,用起来感觉比较僵硬。 后来随着了解增多,尝试了 Typecho、WordPress,Hugo 等主题后,目前还是决定使用 Hugo。它构建速度快,而且安装简单,在 Ubuntu 上只需要一行 sudo apt install hugo 即可,不可谓不简单。因此萌生了移植一个 Hugo 版本主题的想法,刚好可以锻炼一下自己。 欢迎点击 这里 查看我的个人主页。 一些难点 之前从来没有了解过 Hugo 主题的写法以及 Hexo 主题的写法,不过看了一下仓库的组织形式还算好理解。 这类静态博客生成器都是需要写一些模板文件,然后根据配置文件进行个性化构建。 Hugo 的文档 十分的完善,学习就像是学习一门编程语言,里面有很多函数和变量,还有各种条件结构、循环结构等。原主题是采用的 pug + stylus 的方式,而不是传统的 html + css 。不过这两者之间的转换并不麻烦,而且有一些工具可以参考着转换,例如 pug2html 以及 stylus2css 。 后续就参考着一点点的移植就可以,同时我也改写了一下配置文件(使用的 yaml 格式),大概是更易于配置了。 同时,得益于 Hugo 的强大,我很方便的完成了对多语言的支持。 最终效果 目前已经更新到了 v1.1.0 版本,欢迎大家体验,有问题可以及时反馈! 主题链接:https://github.com/zzsqwq/hugo-academia-theme 演示站:https://zzsqwq.github.io/academic-pages-demo/ 英文文档:https://github.com/zzsqwq/hugo-academia-theme/blob/master/README.md 中文文档:https://github.com/zzsqwq/hugo-academia-theme/blob/master/README.zh_cn.md 英文效果图: 中文效果图:

May 3, 2022 · zzsqwq