一、命名空间
命名空间可定义函数,也可嵌套定义空间域,可与头文件中命名空间名相同名字的合成一个空间
namespace sxl
{
int scanf = 10;
int strlen = 20;
int Add(int left, int right)
{
return left + right;
}
//嵌套定义命名空间
namespace N3
{
int c;
int d;
int Sub(int left, int right)
{
return left - right;
}
}
}
- 访问命名空间
//默认访问全局的
printf("%x\n", scanf);
printf("%x\n", strlen);
//指定访问sxl命名的空间
printf("%x\n", sxl::scanf);
printf("%x\n", sxl::strlen);
sxl::Add(1, 2);
sxl::N3::Sub(10, 3);
namespace标准库使用的三种方式:
-
1.指定命名空间(最规范)
使用时都要加std:: -
2.把std整个展开相当于库里面的东西都到全局域(不推荐,日常练习写法)
展开:using namespace std;
缺点:若自己定义的东西与库冲突就没办法解决
- 3.对部分常用的库里面的东西展开(常用方式,1与2的折中方式)
using std::cout;
using std::endl;
::作用域操作符
int a = 0;
int main()
{
int a = 1;
printf("%d\n", a); //1
printf("%d\n", ::a); //0 访问全局域
}
二、输入输出
- ostream 类型全局对象 cout
- istream 类型全局对象 cin
- endl:换行
对比printf、scanf优势:自动识别类型(原理:函数重载、运算符重载)
int main()
{
//对比C语言printf,scanf区别:自动识别类型
int a = 10;
int* p = &a;
printf("%d,%p\n", a, p);
std::cout << a << "," << p << std::endl;
char str[10];
std::cin >> a;
std::cin >> str;
std::cout << a << str << std::endl; //输入sxl123来了,输出结果仍然是这个
return 0;
}
三、缺省参数
缺省参数不能在函数声明和定义中同时出现
- 1.全缺省参数
调用函数时没有指定参数,直接利用函数定义的参数
void TestFunc(int a = 10, int b = 20, int c = 30)
{
cout << "a=" << a << endl;
cout << "b=" << b << endl;
cout << "c=" << c << endl;
}
int main()
{
TestFunc(1, 2, 3);
TestFunc(1);
TestFunc();
TestFunc(1, 3);
return 0;
}
- 2.半缺省参数(默认参数必须从右往左,并且是连续的)
void TestFunc(int a, int b = 20, int c = 30)
{
cout << "a=" << a << endl;
cout << "b=" << b << endl;
cout << "c=" << c << endl;
}
int main()
{
//不能一个都不给值
TestFunc(1, 2,3);
TestFunc(1,2);
TestFunc(1);
return 0;
}
应用:调用时更灵活
struct Stack
{
int* a;
int size;
int capacity;
};
void StackInit(struct Stack* ps, int InitCapacity = 4)
{
ps->a = (int*)malloc(sizeof(int)* InitCapacity);
ps->size = 0;
ps->capacity = InitCapacity;
}
int main()
{
//要多少时就直接指定空间个数
//知道栈里面最多存10个数据
struct Stack st2;
StackInit(&st2, 10);
//不知道栈里面可能存在多少数据
struct Stack st;
StackInit(&st);
}
四、函数重载
- 1.函数重载概念
C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同。
例如:
int Add(int left, int right)
{
return left+right;
}
double Add(double left,int right)
{
return left+right;
}
- 函数重载与缺省值无关
//构成重载,但不能调用,不知道调用哪一个
void f(int a,int b,int c =1)
{}
void f(int a,int b)
{}
-
2.名字修饰
为什么C语言不支持函数重载,C++支持?C++是如何支持的?
程序运行过程:预处理、编译、汇编、链接
(1)预处理
头文件展开、宏替换、去掉注释、条件编译
生成.i文件
(2)编译
检查语法、生成汇编代码
生成.s文件
(3)汇编
把汇编代码转换成二进制机器码
生成.o文件
(4)链接
链接到一起生成可执行文件
生成a.out文件
-
①采用C语言编译,调用函数,去符号表找所在地址是根据编译器命名的函数名去寻找函数
-
②C语言编译器编译后的函数名与代码所写的函数名一致(若多个源文件写相同函数名的函数,则不知道找哪一个函数),C++编译后的函数修饰变成【_Z+函数长度+函数名+类型首字母】,编译器会将函数类型信息添加到修改后的名字中。
即:C语言没办法支持重载,因为同名函数没办法区分,而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持重载。 -
3.extern “C”
在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern “C”,意思是告诉编译器,将该函数按照C语言规则来编译。
例如:
-
tcmalloc是google用C++实现的一个项目,他提供tcmallc()和tcfree两个接口来使用,但如果是C项目就没办法使用,那么它就使用extern “C”来解决。
-
在C++程序中添加下面一行代码,C程序即可调用此函数。若C++程序要调用此程序,添加同样的代码,C++兼容C。
extern "C" void* tcmallic(ussigned int n);
五、引用
-
1.概念
引用:给已存在的变量取了一个别名,引用变量和它共用一块内存空间。 -
2.结构
类型& 引用变量名(对象名)= 引用实体
引用类型必须和引用实体是同种类型的
void TestRef()
{
int a = 10;
int& ra = a;//<====定义引用类型
printf("%p\n", &a);
printf("%p\n", &ra);
}
- 3.引用特性
(1)引用在定义时必须初始化
int a = 10;
int& d = a;
(2)一个变量可以有多个引用
int a = 0;
int& b = a;
int& c = a;
int& d = b;
(3)引用一旦引用一个实体,再不能引用其他实体
void TestRef()
{
int a = 10; //引用初始化
// int& ra; // 该条语句编译时会出错
int& ra = a;
int& rra = a; //多个引用
printf("%p %p %p\n", &a, &ra, &rra);
}
- 4.常引用
int main()
{
const int a = 10; //可读
//int& ra =a; ra引用属于权限放大,此处是可读可写
const int& ra = a; //int& ra =a;不行
int b = 10;
int& rb = b;
const int& crb = b; //crb属于权限缩小,所以可以
int c = 10;
double d = 1.1;
d = c;//隐式类型转换
double& rc = c; //不行
const double& rc = c; //double的临时变量具有常性
return 0;
}
void TestConstRef()
{
const int a = 10;
//int& ra = a; // 该语句编译时会出错,a为常量
const int& ra = a;
// int& b = 10; // 该语句编译时会出错,b为常量
const int& b = 10;
double d = 12.34;
//int& rd = d; // 该语句编译时会出错,类型不同
const int& rd = d; }
- 5.引用的使用(当数值比较大时,以引用作为函数参数的效率会有提高)
(1)做参数
void swap(int& left, int& right)
{
int tmp = left;
left = right;
right = tmp;
}
(2)做返回值
//传值返回,返回c的拷贝
int Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
const int& ret = Add(1, 2);
return 0;
}
//传引用返回,返回传的c的引用
//实际中,出了函数作用域,返回变量就不存在了,不能用引用返回
int& Add(int a, int b)
{
//int c = a + b;
//加static就可以用引用返回
static int c = a + b; //出了作用域值不销毁
return c; //返回c的类型int&
}
int main()
{
int ret = Add(1, 2);
Add(5, 7);
printf("helloworld\n"); //输出为helloworld+随机数
cout << ret << endl; //12
return 0;
}
- 6.越界问题
int main()
{
//越界不一定报错
int a[10] = { 1, 2, 3 };
//越界读基本不报错,因为编译检查不出来
cout << a[10] << endl;
cout << a[11] << endl;
//越界写,可能会报错
//越界,系统是抽查
//a[10] = 0;
a[11] = 0;
a[12] = 0;
return 0;
}
- 7.引用与指针的区别
(1)概念:引用是一个变量的别名,指针存储的是一个变量地址
(2)引用在定义时必须初始化,指针没有要求
(3)引用在初始化时引用一个实体后,就不能再引用其它实体,而指针可以在任何时候指向任何一个同类型实体
(4)没有NULL引用,但有NULL指针
(5)在sizeof中含义不同,引用结果为引用类型的大小,但指针始终是地址空间所占字节个数
(6)引用自加即引用的实体加1,指针自加即指针向后偏移一个类型的大小
(7)有多级指针,但没有多级引用
(8)访问实体方式不同,指针需要显式解引用,引用是编译器自己处理
(9)引用比指针用起来相对更安全
六、内联函数
-
1.定义:以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。
-
2.C语言为了避免小函数(代码量少的函数)建立栈帧的消耗,提供宏函数支持,在预处理阶段展开,为什么C++还要提供inline函数呢?(即宏函数的缺点)
(1)宏函数不支持调试
(2)宏函数语法复杂,容易出错
(3)宏函数没有类型安全的检查
注意:
-
3.宏函数的优点:代码复用性强、 性能比较好
-
4.C++代替宏函数的方式:
(1)常量定义 换用const
(2)函数定义 换用内联函数 -
5.内联函数的特性:
(1)inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的函数不适宜使用作为内联函数。
(2)inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联。
(3) inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。
七、auto
-
1.auto作为一个新的类型指示符来指示编译器
-
2.可以自动识别变量的类型(利用typeid().name()函数判断变量类型)
int main()
{
//int a = 10;
//auto b = a; //类型声明为auto,可以根据a的类型自动推导b的类型
int x = 10;
auto a = &x; //int*
auto* b = &x; //int*
int& y = x; //y的类型是int
auto c = y; //int
auto& d = x; //d的类型是int,但是这里指定了d的引用
cout << typeid(x).name() << endl;
cout << typeid(a).name() << endl;
cout << typeid(b).name() << endl;
cout << typeid(y).name() << endl;
cout << typeid(c).name() << endl;
return 0;
}
- 3.利用auto的范围for
int main()
{
int array[] = { 1, 2, 3, 4, 5 };
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
cout << array[i] << " ";
cout << endl;
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
array[i] *= 2;
//范围for C++新语法遍历,数组都可以
//自动遍历,依次取出array中的元素,赋值给e,直到结束
for (auto& e : array) //auto& 是引用,才能改变array的值 e的名字可以改变,格式不变
{
e *= 2;
}
for (auto ee : array)
{
cout << ee << " ";
}
cout << endl;
return 0;
}