文章目录
一、命名空间
目的:c语言无法解决命名冲突问题,避免命名冲突或名字污染
一个命名空间就定义了一个新的作用域
1.定义命名空间
namespace n1
{
int a;
int b;
int add(int left, int right)
{
return left + right;
}
struct node
{
int val;
node *next; // c++可以不用重命名了
};
}
namespace n2
{
int c;
int d;
int minus(int left, int right)
{
return left - right;
}
}
// rumen.c++和rumen.h中若分别有同一个命名空间 编译器会合并成一个
2.使用命名空间
// 使用方式
// 3. 2.
using namespace n2;
using n1::b;
int main()
{
// 1. ::
printf("%d", n1::a);
// 2.using 引入命名空间中的成员
printf("%d\n", b);
// 3.using 引入整个命名空间
printf("%d\n", c);
return 0;
}
二、C++输入和输出
cout标准输出对象(控制台),cin标准输入对象(键盘),cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,它们都包含在< iostream >头文件中。
1.输入
#include <iostream>
// std是C++标准库的命名空间名
using namespace std;
int main()
{
int a;
//可以自动识别变量类型
cin >> a;
cout << a << endl;
return 0;
}
<<是流插入运算符,>>是流提取运算符
2.输出
#include <iostream>
// std是C++标准库的命名空间名
using namespace std;
int main()
{
cout << "hello world"
<< endl;
return 0;
}
// 在io需求比较高的地方,如部分大量输入的竞赛题中,加上以下3行代码
// 可以提高C++IO效率
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
三、缺省函数
1.概念
缺省函数是声明或定义函数时为参数制定一个缺省值,调用函数时,若无指定实参就用缺省值。
2.分类
全缺省函数:
void f1(int a = 1, int b = 2, int c = 3)
{
cout << "a=" << a << endl;
cout << "b=" << b << endl;
cout << "c=" << endl;
return;
}
半缺省函数:
半缺省函数的缺省参数必须从右向左给出,不能间隔给出。缺省参数不能在函数声明和定义中同时出现,值必须是常量或全局变量。
void f2(int a, int b = 2, int c = 3)
{
cout << "a=" << a << endl;
cout << "b=" << b << endl;
cout << "c=" << endl;
}
四、函数重载
C++允许在同一作用域中声明几个功能类似的同名函数,这些函数的形参列表(形参个数,类型,类型顺序)不同,常用于处理实现功能类似但数据类型不同的问题
1、参数类型不同
// 1、参数类型不同
int Add(int left, int right)
{
cout << "int Add(int left, int right)" << endl;
return left + right;
}
double Add(double left, double right)
{
cout << "double Add(double left, double right)" << endl;
return left + right;
}
void Swap(int *px, int *py)
{
}
void Swap(double *px, double *py)
{
}
2、参数个数不同
// 2、参数个数不同
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}
3、参数类型顺序不同
// 3、参数类型顺序不同
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
cout << "f(char b, int a)" << endl;
}
4.命名空间不同
namespace n1
{
void f1()
{
cout << "f()" << endl;
}
}
void f1()
{
cout << "f()" << endl;
}
5、错误案例
// 下面两个函数构成重载
// f() 但是调用时,会报错,存在歧义,编译器不知道调用谁
void f1()
{
cout << "f()" << endl;
}
void f1(int a = 10)
{
cout << "f(int a)" << endl;
}
// 返回值不同不能作为重载条件,因为调用时也无法区分
void fxx()
{
}
int fxx()
{
return 0;
}
6.名字修饰——C++支持函数重载的原理
在C/C++中,程序运行需要预处理->编译->汇编->链接
每个编译器都有自己的函数名修饰规则,编译完成后,函数名字被修饰(添加了参数信息),C语言并不支持。
两个函数名字和参数若一致,但返回值不同,不构成重构。编译器修饰后的函数名仍然一致,调用时编译器无法区分
五、引用
引用是给已存在变量取个别名,和引用的变量共用同一内存空间
类型&引用对象名=引用实体
void ref()
{
// 引用类型必须和引用实体同一类型
int a = 10;
int &ra = a;
}
1.引用特性
引用在定义时必须初始化,一个变量可以有多个引用,一个引用只有一个实体
int a = 10;
int &ra = a;
int &rra = a;
// int &ra; 是错的
2.常引用
const int &b = 210;
// 编译通常可以成功通过,因为编译器允许基本数据类型之间的隐式转换
double d = 12.34;
const int &rd = d;
3.引用的使用
- 主要用于引用传参和引用做返回值中减少拷贝,提高效率;改变引用对象的同时改变被引用对象
- 引用传参和指针传参功能类似
- 引用自加是引用的实体+1,指针自加是指针向后偏移一个类型的大小
六、内联函数
实现一个ADD宏函数的常见问题
#define ADD(int a, int b) return a + b;
#define ADD(a, b) a + b;
#define ADD(a, b) (a + b)
正确的宏实现
#define ADD(a, b) ((a) + (b))
- 为什么不能加分号?
分号通常用于结束语句,但在宏定义里,如果在 ((a) + (b)) 后加上分号,宏会被解释成两个独立的操作,即 (a) + (b);,而不是期望的加法操作。 - 为什么要加外面的括号?
使用外层括号是为了明确表达式范围,防止宏展开后的代码因为上下文解析而出现问题。如果不使用括号,像 ADD(a, b) 可能会与后续的代码混淆,导致意想不到的结果。加了括号之后,(a) + (b) 就被视为一个整体。 - 为什么要加里面的括号?
内层括号是必要的,因为它确保了 + 运算符优先级高于自变量 a 和 b。如果没有括号,a 和 b 直接跟在运算符后面可能会按照较低的优先级解析,这可能导致错误的结果。
1.使用和特性
inline int Add(int x, int y)
{
int ret = x + y;
return ret;
}
int main()
{
int ret = Add(1, 2);
cout << Add(1, 2) * 5 << endl;
cout << ret << endl;
return 0;
}
- inline是以空间换时间,在编译阶段,会用函数体替换函数调用。可能会使目标文件变大,但是少了调用开销,提高效率
- 函数规模较小,不是递归,不频繁调用的函数可以选择用inline修饰
- inline 声明和定义不要分离,会导致链接错误
- inline函数会在调用的地方展开,所以符号表中不会有inline函数的符号名,不存在链接冲突。可以在同一个项目的不同源文件内定义函数名相同但实现不同的inline函数。
- 使用inline关键字的函数不一定会被编译器在调用处展开,inline只是一种建议,需要看此函数是否能够成为内联函数。比较长的函数,递归函数就算定义为inline,也会被编译器忽略。
2.替代宏
- 宏的优缺点?
优点:
1.增强代码的复用性。
2.提高性能。
缺点:
1.不方便调试宏。(因为预编译阶段进行了替换)
2.导致代码可读性差,可维护性差,容易误用。
3.没有类型安全的检查, - C++有哪些技术替代宏?
1.常量定义 换用const enum
2.短小函数定义 换用内联函数