C++ 基础

命名空间

在 C/C++ 中,变量、函数和类是大量存在的,这些变量、函数和类的名称都保存在全局作用域中,可能会导致冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace 关键字的出现就是针对这种问题的。

定义

定义命名空间,需要使用到 namespace 关键字,后面跟命名空间的名字,然后接一对 { } 即可,{ } 中即为命名空间的成员。

  1. 命名空间中可以定义变量/函数/类
  2. 命名空间可以嵌套
  3. 同一个工程中允许存在多个相同名称的命名空间,编译器会合成为一个命名空间

注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中。

使用方式

标准库定义的所有名字都在命名空间 std 中。

  1. 加命名空间名称及作用域限定符
  2. 使用 using 将命名空间中成员引入
  3. 使用 using namespace 将命名空间中所有成员引入
// 分别对应上面三种方式
std::cin
using std::cin
using namespce std

缺省参数

缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没指定实参则使用默认值,否则使用指定的实参。

分类

  1. 全缺省参数
  2. 半缺省参数
// 分别对应上面两种形式
void test1(int a = 1, int b = 2, int c = 3)
void test2(int a, int b = 2, int c = 3)

注意

  1. 半缺省参数必须从右往左依次给出,不能间隔
  2. 缺省参数不能在函数声明和定义中同时出现
    • 在给定的作用域中一个形参只能被赋予一次默认实参
  3. 局部变量不能作为默认实参
    • 除此之外,只要表达式的类型能转化成形参所需的类型,该表达式就能作为默认实参
    • 用作默认实参的名字在函数声明所在的作用域解析,而这些名字的求值过程发生在函数调用时
// 多次声明同一个函数,为其不同参数添加默认值是合法的,但还是要从右到左给
void test(int a, int b = 2, int c = 3);
void test(int a = 1, int b, int c);

函数重载

自然语言中,一个词可以有多种含义,我们可以通过上下文来判断该词真实的含义,即该词被重载了。

概念

函数重载:是函数的一种特殊情况,C++ 允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表参数个数类型顺序)不同,返回值不作要求。

重载示例

int Add(int left, int right);
long Add(long left, long right);
double Add(double left, double right);

Name Mangling

C

早期 Unix 下的 C 语言因为历史原因规定:C 语言源代码文件中的所有全局变量和函数经过编译以后,相对应的符号前加上下划线 _。在现在 Linux 下的 GCC 编译器中,默认情况已经去掉了在 C 语言符号前面加下划线的方式,而是直接使用函数名。

C 语言不支持函数重载,因为 C 语言直接使用函数名去表示和查找,而重载函数函数名相同。编译的时候,两个函数名相同的函数,在符号表中存在歧义和冲突,其在链接的时候也存在歧义和冲突。

C++

C++ 的目标文件符号表中不是直接用函数名来标识和查找函数。

g++ 的函数名修饰规则:

  • 所有的符号都以 _Z 开头,对于嵌套的名字(在命名空间或在类里面的),后面紧跟 N,然后是各个命名空间和类的名字,每个名字前是名字字符串长度,再以 E 结尾
  • 对于一个函数来说,它的参数列表紧跟在函数名或 E 后面
  • 全局变量和静态变量也有同样的机制,不过修饰并没有使用变量类型

命名修饰

有了函数名修饰规则,只要参数列表不同,符号表存储的修饰后的函数名就不同,也就不存在二义性和冲突了。可以用 nm 命令查看反汇编后函数的名称。

extern “C”

有时候在 C++ 工程中可能需要将某些函数按照 C 的风格来编译,在函数前加 extern “C”,意思是告诉编译器,将该函数按照 C 语言规则来编译。

引用

概念

引用不是新定义一个变量,而是给已存在变量取一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间

语法:类型& 引用变量名 = 引用实体

int a = 10;
int& ra = a;

引用类型必须和引用实体同种类型的

特性

  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能引用其他实体

使用场景

  1. 做参数(提高效率,形参的改变可以影响实参)
  2. 做返回值(提高效率,修改返回变量)

注意:如果函数返回时,出了函数作用域。如果返回对象还未还给系统,则可以使用引用返回;如果已经还给系统了,则必须使用传值返回。

指针和引用的区别

在语法概念上引用就是一个别名,没有独立空间,和其引用体公用一块空间。

在底层实现上是有空间的,引用是按照指针的方式来实现的

引用和指针的不同点

  1. 没有 null 引用,一个引用必须总代表某个对象;指针可以设为 null
  2. 由于引用一定要代表某个对象,因此引用必须有初值;指针可以不初始化,但风险高
  3. 引用总是代表最初获得的那个对象,不能再绑定其他对象;指针可以被重新赋值,指向另一个对象
  4. 在 sizeof 中含义不同:引用是引用的类型所占字节数;指针始终是指针类型所占字节数
  5. 没有多级引用;有多级指针

内敛函数

概念

inline 修饰的函数叫做内联函数,编译时 C++ 编译器会在调用内联函数的地方展开。内联函数没有调用时跳转的开销,不会打断指令流水线,可以提高程序运行的效率。

特性

  1. inline 是一种以空间换时间的做法,省去调用函数的额外开销,增加了内存和磁盘占用,所以代码很长或者有循环/递归的函数不适合作为内联函数
  2. inline 对于编译器而言只是一个请求,编译器会自动优化
  3. inline 不能将声明和定义分离,分离会导致链接错误

功能

宏的优点

  1. 增强代码的复用性
  2. 提高性能

宏的缺点

  1. 不方便调试宏(预处理阶段进行了替换)
  2. 导致代码可读性差,可维护性差,容易误用
  3. 没有类型安全的检查

内联函数替换宏函数

对于形似函数的宏,最好改用 inline 函数替换 #defines。

在类内定义的成员函数会默认加上内敛,内敛声明定义必须一起,否则不起作用。

const 对象替换宏常量

对于单纯常量,最好以 const 对象或 enums 替换 #defines。

C++ 中被 const 修饰的变量:编译器看到变量被 const 修饰,默认其不会改变,若后面有用到该变量会将值放入寄存器中,使用时直接读取寄存器中的数据,而不是到内存中读取。

  • 编译器一般不会为整数型 const 对象在内存中开辟空间,除非指针或引用等必须用它的地址的情况
  • 可以用 volatile 修饰该变量,让每次取值都必须到内存中读取
#include <iostream>
using namespace std;

int main() {
  // volatile const int a = 10;
  const int a = 10;
  int* p = (int*)(&a);
  *p = 20;
  // 此时打印 a 的值是 10, *p 的值是 20
  cout << a << " " << *p << endl;
  return 0;
}
  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值