C++ C++实战笔记

本文是关于C++编程的实战笔记,涵盖了C++的编程范式,如面向过程、面向对象、泛型编程等,并深入讨论了如何写出高质量的C++代码,包括编码风格、预处理阶段的宏定义和条件编译、编译阶段的属性和静态断言,以及面向对象编程的原则。此外,还介绍了C++语言特性,如auto、decltype、const、volatile、智能指针,异常处理和字符串处理。最后,探讨了容器和并发编程,并提到了一些实用的C++网络通信库。
摘要由CSDN通过智能技术生成

前言

本文整理C++实战笔记,是极客时间罗剑锋老师的C++实战笔记笔记,希望大家购买原版,本文如有侵权立删。

  • google c++ code style
    https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/

C++语言的编程范式

“编程范式”是一种“方法论”,就是指导你编写代码的一些思路、规则、习惯、定式和常用语。
C++是一种多范式的编程语言。具体来说,现代C++(11/14以后)支持“面向过程”“面向对象”“泛型”“模板元”“函数式”这五种主要的编程范式。
image.png

  • 面向过程是C++里最基本的一种编程范式。它的核心思想是“命令”,通常就是顺序执行的语句、子程序(函数),把任务分解成若干个步骤去执行,最终达成目标。

  • 面向对象是C++里另一个基本的编程范式。它的核心思想是“抽象”和“封装”,倡导的是把任务分解成一些高内聚低耦合的对象,这些对象互相通信协作来完成任务。它强调对象之间的关系和接口,而不是完成任务的具体步骤。
    在C++里,面向对象范式包括class、public、private、virtual、this等类相关的关键字,还有构造函数、析构函数、友元函数等概念。

  • 泛型编程是自STL(标准模板库)纳入到C++标准以后才逐渐流行起来的新范式,核心思想是“一切皆为类型”,或者说是“参数化类型”“类型擦除”,使用模板而不是继承的方式来复用代码,所以运行效率更高,代码也更简洁。
    在C++里,泛型的基础就是template关键字,然后是庞大而复杂的标准库,里面有各种泛型容器和算法,比如vector、map、sort,等等。

  • 模板元编程,这个词听起来好像很新,其实也有十多年的历史了,不过相对于前三个范式来说,确实“资历浅”。它的核心思想是“类型运算”,操作的数据是编译时可见的“类型”,所以也比较特殊,代码只能由编译器执行,而不能被运行时的CPU执行。
    模板元编程是一种高级、复杂的技术,C++语言对它的支持也比较少,更多的是以库的方式来使用,比如type_traits、enable_if等。

  • 函数式,它几乎和“面向过程”一样古老,但却直到近些年才走入主流编程界的视野。所谓的“函数式”并不是C++里写成函数的子程序,而是数学意义上、无副作用的函数,核心思想是“一切皆可调用”,通过一系列连续或者嵌套的函数调用实现对数据的处理。
    函数式直到C++11引入了Lambda表达式,它才真正获得了可与其他范式并驾齐驱的地位。

说得具体一点,就是要认识、理解这些范式的优势和劣势,在程序里适当混用,取长补短才是“王道”。
如果是开发直接面对用户的普通应用(Application),那么你可以再研究一下“泛型”和“函数式”,就基本可以解决90%的开发问题了;如果是开发面向程序员的库(Library),那么你就有必要深入了解“泛型”和“模板元”,优化库的接口和运行效率。

如何写出好的c++代码

编码阶段的code style

  • 混用这几种中能够让名字辨识度最高的那些优点,就是四条规则:
    变量、函数名和名字空间用snake_case,全局变量加“g_”前缀;
    自定义类名用CamelCase,成员函数用snake_case,成员变量加“m_”前缀;
    宏和常量应当全大写,单词之间用下划线连接;
    尽量不要用下划线作为变量的前缀或者后缀(比如_local、name_),很难识别。
#define  MAX_PATH_LEN  256             //常量,全大写

int g_sys_flag;                        // 全局变量,加g_前缀

namespace linux_sys {
                   // 名字空间,全小写
  void get_rlimit_core();               // 函数,全小写
}

class FilePath final                 // 类名,首字母大写
{
   
public:
  void set_path(const string& str);    // 函数,全小写
private:  
  string m_path;                      // 成员变量,m_前缀 
  int    m_level;                     // 成员变量,m_前缀
};

预处理阶段:宏定义和条件编译

预处理阶段编程的操作目标是“源码”,用各种指令控制预处理器,把源码改造成另一种形式,就像是捏橡皮泥一样。

首先,预处理指令都以符号“#”开头,虽然都在一个源文件里,但它不属于C++语言,它走的是预处理器,不受C++语法规则的约束。
所以,预处理编程也就不用太遵守C++代码的风格。一般来说,预处理指令不应该受C++代码缩进层次的影响,不管是在函数、类里,还是在if、for等语句里,永远是顶格写。

单独的一个“#”也是一个预处理指令,叫“空指令”,可以当作特别的预处理空行。而“#”与后面的指令之间也可以有空格,从而实现缩进,方便排版。

包含文件#include

在写头文件的时候,为了防止代码被重复包含,通常要加上“Include Guard”,也就是用“#ifndef/#define/#endif”来保护整个头文件,

#ifndef _XXX_H_INCLUDED_
#define _XXX_H_INCLUDED_

...    // 头文件内容

#endif // _XXX_H_INCLUDED_

宏定义#define/#undef

宏定义是预处理编程里最重要、最核心的指令“#define”,它用来定义一个源码级别的“文本替换”。
使用宏的时候一定要谨慎,时刻记着以简化代码、清晰易懂为目标,不要“滥用”,避免导致源码混乱不堪,降低可读性。

  • 首先,因为宏的展开、替换发生在预处理阶段,不涉及函数调用、参数传递、指针寻址,没有任何运行期的效率损失,所以对于一些调用频繁的小代码片段来说,用宏来封装的效果比inline关键字要更好,因为它真的是源码级别的无条件内联。

  • 其次,你要知道,宏是没有作用域概念的,永远是全局生效。所以,对于一些用来简化代码、起临时作用的宏,最好是用完后尽快用“#undef”取消定义,避免冲突的风险。像下面这样:

  • 使用宏时,关键是要“适当”,自己把握好分寸,不要把宏弄得“满天飞”。
    用好“文本替换”的功能,

// Nginx中的宏函数
#define ngx_tolower(c)      ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c)
#define ngx_toupper(c)      ((c >= 'a' && c <= 'z') ? (c & ~0x20) : c)
#define ngx_memzero(buf, n)       (void) memset(buf, 0, n)


#define CUBE(a) (a) * (a) * (a)  // 定义一个简单的求立方的宏

cout << CUBE(10) << endl;        // 使用宏简化代码
cout << CUBE(15) << endl;        // 使用宏简化代码

#undef CUBE                      // 使用完毕后立即取消定义

// 另一种做法是宏定义前先检查,如果之前有定义就先undef,然后再重新定义:
#ifdef AUTH_PWD                  // 检查是否已经有宏定义
#  undef AUTH_PWD                // 取消宏定义
#endif                           // 宏定义检查结束
#define AUTH_PWD "xxx"           // 重新宏定义

条件编译 #if/#else/#endif

可以在预处理阶段实现分支处理,通过判断宏的数值来产生不同的源码,改变源文件的形态,这就是“条件编译”。

编译阶段:属性和静态断言

编译是预处理之后的阶段,它的输入是(经过预处理的)C++源码,输出是二进制可执行文件(也可能是汇编文件、动态库或者静态库)。这个处理动作就是由编译器来执行的。
编译阶段的特殊性在于,它看到的都是C++语法实体,比如typedef、using、template、struct/class这些关键字定义的类型,而不是运行阶段的变量。
所以,这时的编程思维方式与平常大不相同。

属性

可以把它理解为给变量、函数、类等“贴”上一个编译阶段的“标签”,方便编译器识别处理;
“属性”相当于编译阶段的“标签”,用来标记变量、函数或者类,让编译器发出或者不发出警告;
“属性”是用两对方括号的形式“[[…]]”,方括号的中间就是属性标签。

[[noreturn]]              // 属性标签,表示这个函数没有返回值
int func(bool flag
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

baiiu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值