c++基础篇

一、命名空间:

1.1命名空间存在的意义:

1.1要知道c++是对c语言缺点的完善,而在c语言中我们是知道,定义变量、函数名或者全域名是不能相同的,否则会产生冲突,但要知道这都是大量存在的,就像一个名字也有很多重名,一个项目,每个人负责不同的模块,也避免不了名字相同(因为我不知道你也用了这个名字),在c语言中就会产生冲突,而且在全域中也可能和库函数中名字相同例如:

1.2命名空间的定义:

 1.2.1命名空间的定义要用到一个关键字就是namespace加命名空间的名字,然后接一个{ },里面就是命名空间的成员。

//这我定义一个名字为xiaoming
namespace xiaoming
{
	//可以定义变量也可以定义函数
	int rand = 10;
	int add(int x, int y)
	{
		return x + y;
	}

	// 同时可以进行嵌套
	namespace hello // 嵌套在命名空间xiaoming的命名空间hello
					// 不同命名空间里的名字可以相同
	{
		int rand = 20;
		int add(int x, int y)
		{
			return x + y;
		}
	}
}

1.3命名空间的使用:

1.3.1命名空间中的成员并不能直接使用,有三种形式使用方式:

<1>加命名空间名称以及作用域符号::

# include <iostream>
using namespace std;

namespace xiaoming
{
	int a = 10;
	int b = 20;
}

int main()
{
	// cout << a << 这种是错误的不能直接使用
	cout << xiaoming::a;
	cout << xiaoming::b;
}

<2>使用using将命名空间某个成员引入:

# include <iostream>
using namespace std;
using xiaoming::a;

namespace xiaoming
{
	int a = 10;
	int b = 20;
}

int main()
{
	cout << a; //这个已经被引入所以可以直接使用
	cout << xiaoming::b;
}

<3>使用using namespace命名空间的引入

# include <iostream>
using namespace std;
using namespace xiaoming;

namespace xiaoming
{
	int a = 10;
	int b = 20;
}

int main()
{
	cout << a;
	cout << b;
}

二、缺省参数:

3.1缺省参数的定义:

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


#include<iostream>
using namespace std;
int Add(int x=10,int y=20)
{
    return x+y;
}
int main()
{
    
    int ret1=Add();  //不穿参数,使用形参默认值
    cout<<ret1<<endl;
    int ret2=Add(1,2)  //穿参数,使用指定实参
    cout<<ret2<<endl;
    return 0;
}

3.2省参数的分类:

全缺省参数:


#include<iostream>
using namespace std;
int Add(int x=10,int y=20,int z=30)
{
    return x+y+z;
}
int main()
{
    
    int ret1=Add(); //可以不传参数
    int ret2=Add(1); //可以传一个参数
    int ret3=Add(1,2); //可以传两个参数
    int ret4=Add(1,2,3); //可以传三个参数
    //但不能像Add(,2,3)或者这样Add(1,,3)传参,必须是从左到右连续滴传参。
          cout<<ret1<<endl<<ret2<<endl<<ret3<<endl<<ret4<<endl;

半省参数:


#include<iostream>
using namespace std;
int Add(int x,int y=20,int z=30)
{
    return x+y+z;
}
 //半省参数必须从右向左依次赋值
int Add1(int x,int y,int z=30)
{
    return x+y+z;
}
//上面两种都是可以的
//但不能中间间隔例如:int Add(int x=10,int y,int z=30)
//或者这样也是不行的 int Add(int x=10,int y,int z)
int main()
{
    int ret1=Add(1,2,3);//可以
    int ret2=Add(1,2);//可以
    int ret3=Add(1);//可以
   // int ret4=Add(); 不可以的x需要传参
同样滴
      int ret5 =Add1(1,2,3);//可以
      int ret6=Add(1,2);//可以
     //  int ret7=Add(1); 不可以因为y没有传参
 
  //半缺省参数是要赋值的
 
    return 0;
}

函数重载:

函数重载的概念:

C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数或类型或类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。


#include<iostream>
using namespace std;
int Add(int x,int y,int z)
{
       return x+y+z;
}
//参数个数不同
int Add(int x,int y)
{
       return x+y;
}
//参数类型不同
double  Add(double x,double y)
{
       return x+y;
}
//参数顺序不同
double  Add(int x,double y)
{
       return x+y;
}
double  Add(double y, int x)
{
       return x+y;
}
int main()
{
     int  ret =Add(1,2,3);
     int  ret1=Add(1,2);
     double   ret2=Add(1.2,2.2);
     double   ret3=Add(1,1.2);
     double   ret4=Add(1.2,1);  //函数重载的作用就是一个函数可以实行多种功能
    cout<<ret<<endl<<ret1<<endl<<ret2<<endl<<ret3<<endl<<ret4<<endl;
    return 0;
}

编译器的工作:
  如果两个函数的参数表中参数的个数或类型或顺序不同,则认为这两个函数是重载。

判断函数重载的规则

如果两个函数的参数表相同, 但是返回类型不同,会被标记为编译错误:函数的重复声明

int my_max(int a,int b)
{
    return a > b ? a : b;
}
unsigned int my_max(int a,int b) // error;
{
    return a > b ? a : b;
}
int main()
{
    int ix = my_max(12,23);
    unsigned int = my_max(12,23); // error;
    reutrn 0;
}

参数表的比较过程与形参名无关

// 声明同一个函数
int my_add(int a,int b);
int my_add(int x,int y);

如果在两个函数的参数表中,只有缺省实参不同,则第二个声明被视为第一个的重复声明

void Print(int *br,int n);
void Print(int *br,int len = 10);


引用:

引用的概念:

引用比较好理解啦,就是给你原有的变量去了一个别名,例如在生活中你的外号,就像叫我小马一样都是别名的意思,编译器不会给引用变量开辟新的内存,他和他引用的变量公用同一个内存空间。

#include<iostream>
using namespace std;
int main()
{
    int a=10;
    int& ra=a;
    printf("%p\n",&a);  //打印a的地址
    printf("%p\n",&ra);    //打印ra的地址 两个地址是相同的 
     return 0;

引用的特性:

<1>引用变量必须初始化。 就像你给一个人起小名要有对象呀

<2>一个变量可以有多个引用。  一个人可以有多个外号什么的

<3>引用一旦引用一个实体,再也不能引用其他实体。

#include<iostream>
using namespace std;
int main()
{
    int a=10;
    int&ra=a; //这是引用的初始化
    // int&ra; //这里没有初始化是不正确的。
    int& rb=a; //一个变量可以有多个引用
    
    return 0;
}

引用的应用:

引用做参数:

通过引用的概念我们可以知道引用是和他的引用变量用同一个地址,所以改变引用就是改变他所引用的变量,就像夸小马文章写的好不就是在夸我吗


#include<iostream>
using namespace std;
void swap(int& x,int& y)
{
    int tmp=0;
    tmp=x;
    x=y;
    y=tmp;
}
int main()
{
    int x=10;
    int y=20;
    swap(x,y);
    cout<<x<<' '<<y<<endl;
    return 0;
}

引用做返回值:

#include<iostream>
using namespace std;
int& Add(int x,int y)
{
    static int ret=x+y; //想想这里为什么用static
    return ret;
}
int main()
{
   int ret=Add(1,2);
    cout<<ret<<endl;
    return 0;
}

在这里我们想一下为什么要用static 要是不用static的后果是什么呢? 在我们讲函数栈帧的创建和销毁的时候已经知道,局部变量是储存在栈区的,而栈区是随着函数调用结束后是会被销毁的, 但引用是和引用对象一个地址的,static是把局部变量从栈区存放到静态区,这样随着函数的调用结束后不会被销毁,因此返回的时候还能够找到,要是不用static当返回去寻找的时候是找到的就会是随机值。就好比你住个酒店,而当你退房了之后,发现你的包裹没有拿,而当你返回去的时候,你就无法确定你的包裹还在,他可能还在就是没有被收拾,但有可能你住的酒店已经被其他用户住给扔掉了,这都是有可能的,而static就是把包放在一个储存的东西的地方,你再去这个地方拿就行了

引用和指针的区别:

引用就是引用对象的一个别名,而指针是变量的地址

引用必须初始化,而地址不需要初始化。

引用在初始化一个引用对象后就不能在引用其他变量了,而指针确可以在任何时候指向同类型的地址。

用自身加一是引用对象加一,而指针加一则是地址加一。

指针有多级指针,而引用没有

引用的使用场景

做参数

因为实参给形参传值和传地址都需要传一份值/地址的拷贝,引用传参可以减少拷贝,提高效率

#include <iostream>
using namespace std;
int add(int& a,int& b)
{
  return  a+b;
}
int main()
{
    add(1,2);
}

2.函数返回值

int& Count()
{
    int n = 0;//变量n没有加static,返回的变量n可能会被覆盖
    n++;
    cout << " & n:" << endl;
    return n;
}
int main()
{
    int& ret = Count();
    cout << ret << endl;
    cout << "&ret:" << ret << endl;
    cout << ret << endl;
}

首先我们来分析一下没有引用的传值输入

普通的传值返回需要把返回值n给一个函数类型int的临时变量(函数类型就是返回值类型),再把临时变量给ret。

这里设计一个临时变量的原因:

为当函数Count里执行完各种代码后,返回n,等出了Count函数的作用域后n就会销毁,所以不能直接把n给ret,需要一个临时变量。

如何证明返回时存在临时变量呢?:

如果你用int& ret 接收,写成int& ret = Count(); 发现无法运行,因为临时变量有常性,所以需要写成const int& ret = Count(); 才能通过。

我们再来看看传引用返回

当用引用接收引用返回时:这里ret和n的地址一样,也就意味着ret其实就是n的别名。但是因为n出作用域不会立即被覆盖,所以第一次通过ret可以打印是1,当打印第二次时,因为前面已经调用过一次打印函数,已 “销毁” 的Count函数栈帧在此时被打印函数覆盖,再打印ret就会是随机数了!

即:

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


用static修饰n后:用static静态变量使n只初始化一次且改变其生命周期,把n放进了静态区,这样n就一直存在,就可以通过ret找到n了,再怎么打印ret都是1.

内联函数:

内敛函数存在的意义:

 在c语言中调用一个函数要经过栈帧的创建和销毁,而当一个函数调用次数过多的时候就会降低程序运行的效率。这里的解决办法是什么呢?在c语言中有一个解决的方法就是宏函数。想必大家也忘了宏函数的写法了,这里我写一个宏函数的代码。


#include<iostream>
using namespace std;
#define Add(x,y)   ((x)+(y))
int main()
{
    int ret=Add(1,2);
    cout<<ret<<endl;
    return 0;
}

为什么宏函数解决了效率呢,要知道一个程序运行的完整运行,是有预处理,编译,汇编,链接四个过程的,而宏函数是在预处理已经完成了。但宏函数已经解决了c栈帧创建和销毁的缺点,为什c++还会创建一个内敛函数呢?要知道虽然宏函数解决了效率问题,但它本身也有自身的缺点,我们可以看出宏函数还是很容易写错的,我这个是比较简单的,要是复杂一点就是很容易就写错的,而宏函数因为在预处理就已经结束了,所以是没有办法调试的,并且他也没有类型安全的检查,因此c++就用内敛函数来解决这个问题。

内敛函数的定义:

inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调 用建立栈帧的开销,内联函数提升程序运行的效率。


#include<iostream>
using namespace std;
inline int Add(int x,int y)
{
     return x+y;
}
int main()
{
    int ret =Add(1,2);
    cout<<ret<<endl;
    return 0;
}

内敛函数和普通函数功能相同就是在函数inline同时也具有了宏函数的一些功能就是不参与编译,在预处理就已经完成了。

内敛函数特性:

inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。

inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值