1.命名空间
1.1为什么???
使用命名空间的目的是对标识符进行本地化,避免命名冲突或名字污染。
1.2怎么做???
命名空间的定义:
namespace lhh //lhh是命名空间的名字
{
}
*命名空间是可以嵌套
*同一个工程中允许存在多个相同名字的命名空间,编译器最后会自动合成一个。ps:一个工程中的test.h和test.cpp中的相同名字的命名空间会合并成一个。
注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中。
命名空间的使用:
namespace lhh
{
// 命名空间中可以定义变量/函数/类型
int a = 0;
int b = 1;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
int main()
{
// 编译报错:error C2065: “a”: 未声明的标识符
printf("%d\n", a);
return 0;
}
//因此a不能在命名空间外直接使用
法一:命名空间名+作用域限定符
int main()
{
printf("%d\n",lhh::a);
return 0;
}
法二:使用using将命名空间中某个成员引入
using lhh::b
int main()
{
printf("%d\n", lhh::a);
printf("%d\n", b);
return 0;
}
法三:使用using namespace 命名空间名称 引入
using namespce lhh;
int main()
{
printf("%d\n", lhh::a);
printf("%d\n", b);
Add(10, 20);
return 0;
}
std是c++标准库的命名空间,c++将标准库的定义实现都放在这个命名空间中。
#include<iostream> ---cout;cin.
c++的输入输出更加方便,不需要手动控制变量类型.
3.缺省参数
是函数在声明或定义时为函数的参数指定的一个缺省值。在调用函数时,如果没有指定的实参则采用该形参的缺省值,否则使用指定的实参。
有全缺省参数和半缺省参数;
注意:
*半缺省参数必须从又往左缺省,不能间隔着给。
*缺省参数不能在函数声明和定义中同时出现。
4.函数重载
是什么???
c++允许在同一作用域中声明几个功能类似的同名函数,他们的形参列表(参数的个数 或 类型 或 类型顺序)不同。常用来处理实现功能类似数据类型不同的问题常用来处理实现功能类似数据类型不同的问题。
4.1 c++支持函数重载的原理
在c/c++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。
通常一个项目又好多个头文件和源文件组成。
如果说,出现以下情况:【当前a.cpp中调用了b.cpp中定义的add函数时,编译后链接前,a.o的目标文件中没有add的函数地址,因为add是在b.cpp中定义的,所以add的地址在b.o中,应该怎么办???】
答案是:在链接阶段,链接器看到a.o调用Add,但是没有Add的地址,就会到b.o的符
号表中找 Add 的地址,然后链接到一起 。 现在的问题是,如何去找到add函数(使用哪个名字去找?)
*每个编译器都有自己的函数名修饰规则。Windows下的vs修饰规则过于复杂,而Linux下的g++的修饰规则简单。两者原理相同,下面我们用Linux系统做演示:
结论:在Linux下,采用gcc编译完成之后,函数名字的修饰没有改变。
采用g++编译完成之后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。
因此:这里就理解了c语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
补充:在进行多人混合开发的模式下,,由于c和c++编译器ui函数名字修饰规则不同,可能会导致链接失败,这种情况下,我们可以在函数前加extern"C",意思是告诉编译器按照c语言规则来编译。
5.引用
5.1定义:
引用就是给已经存在的变量取别名,编译器不会为引用变量开辟内存空间,它和他的引用变量公用一块内存空间。
int a = 10;
int& ra = a;
int& rra = a;
const int a =10;
const int& b = a;
//错误写法
int& ra;
注意:1、引用和实体是同类型的。
2、引用在定义时必须要初始化。
3、一个变量可以有多个别名。
4、一旦引用了一个实体,就不能再引用其他实体。(即一个别名只能给一个实体)
5.2使用: 作参数、作返回值
首先,我们先看一段代码,代码的输出结果是什么??
int& Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int& ret = Add(1, 2);
Add(3, 4);
cout << "Add(1, 2) is :"<< ret <<endl;
return 0;
}
结果是随机值。
add函数运行结束之后,该函数对应的栈空间就被回收了,即c变量就没有意义了,在main中ret引用add函数返回值,实际引用的就是一块已经被释放的空间。
注意:1、函数运行时,系统需要给该函数开辟独立的栈空间,用来保存该函数的形参、局部变量以及一些寄存器信息等。 2、函数运行结束后,该函数对应的栈空间就被系统回收了。 3、空间被回收指的是该栈空间暂时不能使用,但内存还在。
如果函数返回时,出了安徽念书作用域,如果返回对象还在(还没有还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。
传值和传引用的效率区别:传值,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此传值的效率特别低。
引用和指针的区别:在语法上,引用就是一个别名,没有独立空间,和其引用实体公用同一块空间。 在底层上,实际是有空间的,因为引用时按照指针方式来实现的。
6.内联函数
6.1为什么???
函数在调用时是有时间和空间的开销的,程序在执行一个函数之前需要做一些准备工作,要将实参、局部变量、返回地址以及很多的寄存器都压入栈中,然后才能执行函数体中的代码;等到函数体执行完毕,还要清理现场,将之前压入栈的数据都出栈,才能继续执行函数调用之后的代码。如果函数体代码比较多,需要很长的执行时间,那么函数调用机制占用的时间可以忽略;如果函数短小,频繁的调用在调用上的开销就是不可忽视的。
6.1概念:
补充:宏的优缺点?优点:1. 增强代码的复用性。2. 提高性能。缺点:1. 不方便调试宏。(因为预编译阶段进行了替换)2. 导致代码可读性差,可维护性差,容易误用。c++有哪些技术代替了宏?1、常量定义换用const enum2、短小函数定义 换用内联函数
使用宏代码的最大缺点就是容易出错,预处理在拷贝宏代码时常常产生意想不到的边际效应。
内联机制既具备了宏代码的效率又增加了安全性,而且可以自由操作类的数据成员。因此,应该尽量使用内联去代替宏代码。
注意:要在函数定义的时候添加inline关键字,在函数声明时添加虽然没有错,但是编译器会自动忽略。
7.auto关键字
7.1为什么??
1、类型难以拼写 2、含义不明确导致容易出错
7.2是什么??
作为一个新的类型指示符来只是编译器,auto声明的变量必须有编译器在编译时推到而得
使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推到auto的实际类型。因此,suto并非是一种类型的声明,而是一个类型声明时的占位符,编译器的编译期会将auto替换为变量实际的类型。
注意:1、用auto声明指针类型时,用auto和auto*没有任何区别,但是用auto声明引用类型的时候必须要加&
int x = 10;
auto a = &x;
auto* b = &x; //声明指针类型的时候加* 与不加都可以
auto& c = x; //引用类型必须加
2、auto不能作为函数的参数类型,编译器无法进行推导
3、auto不能用来直接声明数组
8.基于范围的for循环
1、范围for的语法
void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
for(auto& e : array)
e *= 2;
for(auto e : array)
cout << e << " ";
return 0;
//for循环后的括号由 :分为两部分,第一部分时范围内用于迭代的变量,第二部分时表示迭代的范围
}
2、使用条件: ***for循环跌代的范围必须是确定的:对于数组而言时第一个元素到第二个元素的范围,对类而言是提供begin与end的方法 ***迭代的对象要实现++和==的操作。