C++入门

文章目录

  • C++入门
    • 1.C++关键字
    • 2.命名空间
      • 2.1命名冲突
      • 2.2命名空间可以定义变量/函数/结构体
      • 2.3命名空间可以嵌套
      • 2.4合并
      • 2.5定义
      • 2.6命名空间的使用(三种方式)
    • 3.C++输入&输出
    • 4.缺省参数
      • 4.1缺省参数概念
      • 4.2缺省参数分类
        • 4.2.1全缺省参数(所有参数都给了缺省值)
        • 4.2.1半缺省参数(部分参数给了缺省值--必须从右往左连续缺省)
      • 4.3缺省参数不能在声明和定义中同时出现
    • 5.函数重载
      • 5.1函数重载概念
        • 5.1.1类型不同
        • 5.1.2参数个数不同
        • 5.1.3顺序不同
    • 6.引用
      • 6.1引用概念
      • 6.2引用特性
      • 6.3引用的使用
        • 6.3.1.引用做参数
        • 6.3.2传引用返回
      • 6.4效率
      • 6.5常引用
      • 6.6指针和引用的区别
    • 7.内联函数
    • 8.auto关键字(C++11)
    • 9.基于范围的for循环(C++11)
    • 10.指针空值---nullptr(C++11)

C++入门

1.C++关键字

C++入门_第1张图片

2.命名空间

在C/C++中,变量,函数和后面要学到的类都是大量存在的,这些变量,函数和类的名称都将存在于全局变量作用域中,可能会导致很多冲突,使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的
命名冲突
1.我们定义自己的变量,函数可能跟库函数里面重名
2.进入公司项目组以后,做的项目通常比较大。多人协作,两个同事写的代码,命名冲突
3.C语言没有办法很好的解决这个问题
C++提出了一个新语法叫命名空间

2.1命名冲突

//这段代码就是正确的
#include
int rand = 0;
int main()
{
	printf("%d", rand);
	return 0;
}
//但是当我们加了#include的时候,这段代码就会报错,原因是stdlib.h这个库函数里面,包含了rand函数,在编译的时候,编译器不知道rand到底是以变量的形式存在的还是以函数的形式存在的,命名冲突了
#include
#include
int rand = 0;
int main()
{
	printf("%d", rand);
	return 0;
}

所谓命名空间就是自己开辟了一块空间,跟全局域和局部域互不干扰,用的时候再拿出来
解决办法(命名空间定义在全局域)

#include
#include

namespace bai  
//开辟出来了一个叫bai的空间域
//rand存在这个域里面,要它的时候再用
{
	//rand是全局变量,放在静态区的,不能被修改
	int rand = 0;
	rand=10//不能赋值
}
int main()
{
	printf("%d", bai::rand);
	//:: 是域作用限定符,这个意思就是rand取的是bai这个域里面的
	//全局域表示--printf("%d", ::rand);
	//这个打印出来的是函数的地址
	return 0;
}

下面代码是错的,供参考

#include
namespace bai  
//开辟出来了一个叫bai的空间域
//rand存在这个域里面,要它的时候再用
{
	int rand = 0;
}
int main()
{
	printf("%d", rand);
	//全局域相当于地球,bai相当于无敌小盒子(怎么都不会被影响),把rand放在小盒子里了,地球上发生的其他事情就跟小盒子里面的东西没关系了,想要小盒子里面的东西的时候打开它就行了
	//rand先在局部变量找自己,没有,然后再在全局变量找自己,也没有(如果全局和局部同时有,优先用局部的)
	//最后会报错
	return 0;
}

2.2命名空间可以定义变量/函数/结构体

namespace bai  
{
	int rand = 8;
	int Mul(int x, int y)
	{
		return x * y;
	}
	struct S
	{
		int a;
	};
}
int main()
{
	bai::rand = 8;
	bai::Mul(1, 2);
	struct bai::S s;
}

2.3命名空间可以嵌套

namespace bai  
{
	int bai = 8;
	int Mul(int x, int y)
	{
		return x * y;
	}
	namespace Bai
	{
		int Bai = 18;
		int Add(int x, int y)
		{
			return x + y;
		}
	}
}
int main()
{
	bai::bai = 8;
	bai::Mul(1, 2);
	bai::Bai::Bai = 18;
	bai::Bai::Add(1, 2);
}

2.4合并

同一个工程中允许存在多个相同名称的命名空间,编译器最后会合并成同一个命名空间中
比如:一个工程Test.h和Test.cpp中的两个名叫M1的命名空间会被合并成一个

2.5定义

命名空间里面可以只放定义, 不放声明

2.6命名空间的使用(三种方式)

1.加命名空间名称及作用域限定符,指定作用域–做到了最好的隔离,但是使用不方便

namespace bai 
{
	int a = 10;

}
int main()
{
	printf("%d", bai::a);
}

2.用using将命名空间中某个成员引入,用于展开命名空间中常用的

namespace bai 
{
	int a = 10;
	struct b
	{
		int c;
		int d;
	};

}
using bai::a;
int main()
{
	printf("%d", a);
}

3.使用using namespace 命名空间名称 引入–全部展开,用起来方便了,隔离失效了(慎用)

namespace bai 
{
	int a = 10;
	struct b
	{
		int c;
		int d;
	};

}
using namespace bai;
int main()
{
	printf("%d", a);
}

3.C++输入&输出

#include
//C++的库函数,相当于C语言#include
using namespace std;
//C++的库函数都包含在一个名叫std的域里面,将这个域展开之后才可以用C++的东西
cout--流插入运算符
cin--流提取运算符
int main()
{
	cout << "hello" << endl;
	//cout是输出流,相当于printf
	//endl是换行,相当于‘\n’
	
	return 0;
}
#include
using namespace std;
int main()
{
	cout << "hello" << endl;
	int i = 10;
	double d = 1.11;
	//自动识别类型,不需要像printf那样指定类型输出了
	cout << i << " " << d << endl;

	return 0;
}
#include
using namespace std;
int main()
{
	cout << "hello" << endl;
	int i = 10;
	double d = 1.11;
	//cin相当于scanf
	cin >> i >> d;
	//自动识别类型
	cout << i << " " << d << endl;


	const char* str = "hello world";
	cout << str << endl;
	return 0;

	

	return 0;
}

std命名空间使用惯例:
std是c++标准库的命名空间,如何展开std使用更合理呢?
1.在日常练习中,建议直接using namespace std即可,这样就很方便
2.using namespace std展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型/对象/函数,就存在冲突问题。那么这个问题在日常练习中很少出现,但是在项目代码较多,规模大的项目开发中就很容易出现。所以建议在项目开发中使用像std::cout这样类型的命名空间展开方式

4.缺省参数

4.1缺省参数概念

通俗一点的来说就是给函数里面的参数赋值
看例子

void fun(int a = 0)//我们平时的函数的参数,a不被赋值,缺省就相当于给a设置了一个默认值
{
	cout << a << endl;
}
int main()
{
	fun();//没有传参时候,使用参数的默认值
	fun(10);//传参时,使用指定的参数值
	return 0;
}

4.2缺省参数分类

4.2.1全缺省参数(所有参数都给了缺省值)

void fun(int a = 10, int b = 20, int c = 30)
{
	cout << "a=" << a << " ";
	cout << "b=" << b << " ";
	cout << "c=" << c << " " << endl;
}
int main()
{
	fun();//打印出来 10 20 30
	fun(1);//传参是从左往右,只有一个,就给a传参,打印出来是1 20 30
	fun(1, 2);//打印出来是 1 2 30
	fun(1, 2, 3);//打印出来是1 2 3
	return 0;
}

4.2.1半缺省参数(部分参数给了缺省值–必须从右往左连续缺省)

void fun(int a , int b = 20, int c = 30)//缺省两个
{
	cout << "a=" << a << " ";
	cout << "b=" << b << " ";
	cout << "c=" << c << " " << endl;
}
void fun(int a , int b , int c = 30)//缺省一个
{
	cout << "a=" << a << " ";
	cout << "b=" << b << " ";
	cout << "c=" << c << " " << endl;
}

以下是错误示范

//示范1
void fun(int a , int b = 20, int c )
{
	cout << "a=" << a << " ";
	cout << "b=" << b << " ";
	cout << "c=" << c << " " << endl;
}
//示范2
void fun(int a=10 , int b , int c = 30)
{
	cout << "a=" << a << " ";
	cout << "b=" << b << " ";
	cout << "c=" << c << " " << endl;
}

4.3缺省参数不能在声明和定义中同时出现

可以在声明中出现或者在定义中出现,就是不能同时出现

5.函数重载

5.1函数重载概念

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

5.1.1类型不同

int add(int a, int b)
{
	return a + b;
}
double add(double a, double b)
{
	return a + b;
}
int main()
{
	add(1,2);
	add(1.1,2.2);
}

5.1.2参数个数不同

void A()
{
	cout << "A()" << endl;
}
void A(int a)
{
	cout << "A(int a)" << endl;
}
int main()
{
	A();
	A(1);
	return 0;
}

5.1.3顺序不同

void A(int a,double b)
{
	cout << "A(int a,double b)" << endl;
}
void A(double a,int b)
{
	cout << "A(double a,int b)" << endl;
}
int main()
{
	A(1,2.2);
	A(2.2,1);
	return 0;
}

注意:
1.如果类型,顺序,个数都相同,但是返回值不同,不能构成重载,因为调用的时候不能区分
2.缺省值不同也不能构成重载
3.void f()与void f(int a=0)构成重载,但是调用存在歧义
4.在C/C++中,一个程序要运行起来,需要经历:预处理(预编译)-编译-汇编-链接
C++入门_第2张图片

6.引用

6.1引用概念

引用不是新定义一个变量,而是给已存在的变量取了一个别名,编译器不会给引用变量开辟内存,它和它引用的变量共同用一块内存空间
比如妻子,你也可以叫媳妇,老婆,都指的同一个人

#include
using namespace std;
int main()
{
	int a = 1;
	int& b = a;//给a取了个别名叫b,这里的&不是取地址
	int* c = &b;//这个&才是取地址


	a = 2;//这时候a和b都是2,他们都代表同一个东西,a改了,b也就改了
	b = 3;//相同道理,a和b都是3
	return 0;
}

6.2引用特性

1.引用在定义时必须初始化

int a = 1;
int& b = a;//不能直接int& b;

2.一个变量可以有多个引用

	int a = 1;
	int& b = a;
	int& c = a;
	int& d = c;
	//归根到底都是给1这个玩意取名字

3.引用一旦引用一个实体,再不能引用其他实体

	int a = 1;
	int& b = a;
	int c = 2;
	b = c;//这里是把c赋值给b,而不是让b变成c的别名

6.3引用的使用

6.3.1.引用做参数

首先是c语言指针版本传参

void swap(int* px, int* py)
{
	int tmp = *px;
	*px = *py;
	*py = tmp;
}
int main()
{
	int x = 10;
	int y = 20;
	swap(&x, &y);
	return 0;
}

下面是引用传参

void swap(int& a, int& b)//a是x的别名,b是y的别名
//交换ab就相当于交换xy
{
	int tmp = a;
	a = b;
	b = tmp;
}
int main()
{
	int x = 10;
	int y = 20;
	swap(x, y);
	return 0;
}

6.3.2传引用返回

先拿普通函数来说,这时候返回的c,并不是直接返回c,而是返回的时候,把c的值放在了一个临时空间中,返回的是这个临时空间的值,而出了add函数之后,add函数的栈帧就被销毁了,c也就跟着销毁了,只能返回了c的临时拷贝的值

int add(int x, int y)
{
	int c = x + y;
	return c;
}
int main()
{
	int a = 1;
	int b = 2;
	int ret=add(a, b);
	return 0;
}

再看传引用返回(这段代码错误,供参考对比)

//传引用返回的意思就是返回了c的别名
不会生成c的拷贝返回,直接返回了c的别名
int& add(int x, int y)
{
	int c = x + y;
	return c;
}
int main()
{
	int a = 1;
	int b = 2;
	int ret=add(a, b);
	return 0;
}
//当前代码的问题
//1.存在非法访问,因为add(1,2)的返回值是c的引用,所以add
//的栈帧销毁了以后,回去访问c的位置
//2.如果add函数栈帧销毁,清理空间,那么取c值的时候取到的就是随机值,
//给ret的就是随机值,当然这个取决于编译器实现
//vs下没有清理

如果函数返回时,出了函数作用域之后,如果返回对象还在(还没还给系统),则可以使用引用返回

int& a()
{
	static int a = 0;
	return a;
}

如果已经还给系统了,则必须使用传值返回

int add(int x, int y)
{
	int c = x + y;
	return c;
}

6.4效率

传引用在数据很多且出了作用域不被销毁时,效率相对于传值提高

6.5常引用

int main()
{
	//权限放大--不可以
	const int a = 10;
	int& b = a;

	//权限不变--可以
	const int c = 10;
	const int& d = c;

	//权限缩小--可以
	int e = 10;
	const int& f = e;
	return 0;
}

假设x是一个大对象或者后面学习深拷贝的对象
//那么尽量用引用传参,减少拷贝,如果f函数中不改变x
//建议尽量用const引用传参

void f(const int& x)//如果没有const,传a传不过来,但是c可以传过来
{
	cout << x << endl;
}
int main()
{
	const int a = 10;
	int c = 20;
	f(a);
	f(c);
	return 0;
}
#include
using namespace std;
int main()
{
	double d = 1.1;
	int a1 = d;//对--d赋值给a1不是直接复制的,是有一个临时变量把d的值拷贝给a
	int& a2 = d;//不对
	const int& a2 = d;//对--相同道理,会产生一个临时变量,a3是临时变量的别名
					  //临时变量是一个右值,不能被改变,要加上const修饰
	return 0;
}

右值----通常不能被修改,表达式产生的临时变量,常量就是右值

a=10;
//10就是右值

const+类型+&----可以接收各种类型的对象
使用引用传参,如果函数中不改变参数的值,建议用const&

6.6指针和引用的区别

C++入门_第3张图片
在这里插入图片描述

7.内联函数

调用函数,需要建立栈帧,栈帧中要保存一些寄存器,结束后又恢复
//可以看到这些都是有消耗的
//对于类似以下代码频繁调用的小函数,能否优化一下

int add(int a, int b)
{
	return a + b;
}
int main()
{
	int a = 1;
	int b = 2;
	add(a, b);
	add(a, b);
	add(a, b);
	add(a, b);
	add(a, b);
	add(a, b);
	add(a, b);
	add(a, b);
	return 0;
}

为此,有了内联函数
有了内联函数,就不需要c语言的宏,因为宏很复杂,很容易出错

inline int add(int a, int b)
{
	return a + b;//相当于直接在main函数的add函数那里展开了,不需要再调用了
}
int main()
{
	int a = 1;
	int b = 2;
	add(a, b);
	
	return 0;
}

特性:
1.内联(inline)是一种以空间换时间的做法,省去调用函数额外开销,所以代码很长(10行以上算长)或者有递归的函数不适宜用作内联函数
2.inline对于编译器只是一个建议,编译器会自动优化,如果定义为inline的函数很长或者递归函数等等,编译器优化时会忽略掉内联
3.inline不建议声明和定义分离,分离会导致链接错误,因为inline被展开,就没有函数地址了,链接就会找不到
图片对应特征3
C++入门_第4张图片
结论:短小的,频繁调用的函数建议使用内联函数(inline)

8.auto关键字(C++11)

int main()
{
	//自动推导变量类型
	auto a = 10;
	auto b = 'a';
	auto c = 1.11;

	//typeid----打印变量类型
	cout << typeid(a).name() << endl;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	//auto会自动省略const属性
	//比如
	const int a=1;
	auto b=a;
	这个时候auto自动识别就是int,而不是const int
	return 0;
}
	int x = 10;
	auto a = &x;//a的类型是int*
	auto* b = &x;//b的类型是int*
	auto& c = x;//c的类型是int,是x的别名
void fun(auto a)//错的
{

}
int main()
{
	int b = 10;
	auto a;//auto不能独立定义
	//应该改成auto a=b;




	//auto也不能作为函数参数
	fun(b);



	//auto也不能定义数组
	auto c[]={1,2,3};//错的
	return 0;
}

9.基于范围的for循环(C++11)

int main()
{
	int arr[] = { 1,2,3,4,5 };
	//自动依次取数组arr中的每个元素赋值给a(a只是变量名字,取别的名字也可以)
	for (auto a : arr)
	{
		cout << a << endl;
	}


	for (int a : arr)//这样也可以,但是auto更爽
	{
		cout << a << endl;
	}

	//把数组中每个值加1
	for (auto& a : arr)
	{
		cout << a << endl;
	}
	return 0;
}
在这里插入代码片void Print(int a[])
{
	for (auto x : a)//范围for这么用就不行,范围for里面必须是数组名,这里的a是指针变量
	{
		cout << x << endl;
	}
}
int main()
{
	int arr[] = { 1,2,3 };
	Print(arr);
	return 0;
}

10.指针空值—nullptr(C++11)

void fun(int)
{
	cout << "int" << endl;
}
void fun(int*)
{
	cout << "int*" << endl;
}
int main()
{
	int* p1 = NULL;
	int* p2 = 0;
	fun(0);//0是int类型,NULL是int*的指针类型,这两句我们预期是第一句打印int
	//第二句打印int*
	//可是最后都打印出来了int
	fun(NULL);
	return 0;
}

C++入门_第5张图片
原因就是在C++98中,NULL是一个宏,在后台被定义成了0,就是int类型,NULL类型不够清楚,所以C++11中定义了一个新的指针nullptr
它的类型就是int*

void fun(int)
{
	cout << "int" << endl;
}
void fun(int*)
{
	cout << "int*" << endl;
}
int main()
{
	int* p = nullptr;
	fun(nullptr);
	return 0;
}

C++入门_第6张图片
C++入门_第7张图片

你可能感兴趣的:(c++,开发语言,c语言)