C++入门

发布于:2025-08-09 ⋅ 阅读:(17) ⋅ 点赞:(0)

目录

一、namespace命名空间域

1.1namespace价值

1.2namespace的定义(使用)

1.3命名空间使用

二、C++输出和输入

三、缺省参数

四、函数重载

五、引用

5.1介绍

5.2引用的使用

5.3功能

5.4特性

5.5const引用权限问题

5.6与指针区别

六、内联函数

6.1介绍:传统宏函数优缺点引申内联函数概念

6.2内联函数的使用

6.3内联函数缺点

七、nullptr


一、namespace命名空间域

1.1namespace价值

        为了解决C语言中命名冲突,C++中引用了namespace来解决在同一空间内中出现相同变量名。部分头文件中有相同的变量名冲突问题,namespace用于区分全局域和命名空间域之间的变量。程序在寻找变量时,会首先在局部中寻找,其次然后就算全局域(ps:命名空间域系统不会主动去找,需要自己声明,后面会讲解)。

1.2namespace的定义(使用)

        namespace的定义实质上就是定义一个域,与全局域区分开,namespace定义和结构体定义一样(只不过结构体的花括号后面有分号,这里没有分号)。namespace定义的域名,遇到有全局域和namespace定义的域时,首先会使用全局域,当然这里要是全局域中没找到这个变量,及时namespace内有变量,也会报错,而当你想要使用namespace对应的域时,那就必须   (域名::变量)  这样使用才能访问到这个域的变量,这样就能使得不同的域使用相同的同名的变量(解决同名的冲突问题)。

无全局域、无局部域

#include<iostream>
namespace bear
{
	int hight = 180;//cm
	int weight = 75;//kg
}//没分号

int main()
{
	std::cout << bear::hight << '\n' << bear::weight << std::endl;
	return 0;
}

一个变量在全局域、局部域没命名时使用过程中需要使用一个::作用域解析操作符(一般不使用时是先寻找局部再全局)

有全局域、有局部域

#include<iostream>

namespace bear
{
	int hight = 180;
	int weight = 75;//kg
}

int hight = 160;
int weight = 60;
int main()
{
	int hight = 170;
	int weight = 70;
	std::cout << bear::hight << '\n' << bear::weight << std::endl;
	return 0;
}

 和上面结果是一样的,180和75(如果没用作用域操作符那就会是170和70)。如果在这里我想要使用全局变量,那就直接使用::hight来使用(系统会将全局域为无名,从而想使用全局时,那就能::变量)。

	std::cout << ::hight << '\n' << ::weight << std::endl;

        C++中有局部域,全局域,命名空间域,类域,除了局部域和空间域有生命周期概念外,命名空间域和类域没有这一概念,但是消除这一变量也是全局,也就是生命周期和全局一样,但是就是没这一概念。namespace用于全局变量。namespace也能嵌套,在访问过程中也要使用 (域名::嵌套域名 ::变量)。

namespace Class
{
	namespace lisi
	{
		int hight = 180;
		int weight = 75;//kg
	}
	namespace zhangsan
	{
		int hight = 188;
		int weight = 80;
	}
}

int main()
{

	std::cout << Class::lisi::hight << '\n' << Class::zhangsan::hight << std::endl;
	return 0;
}

1.3命名空间使用

        项目工程中多文件定义相同名域的namespace不会冲突。一般练习时,一直使用命名空间成员时,可以展开命名空间中所有成员,用using namespace 域名 来展开(放全局里面,项目不推荐容易冲突),当然using的使用也可以在命名空间内变量(用于有一个变量或函数使用频繁)格式为using namespace 域名(部分展开时using 域名::变量);一个是全部展开一个是部分展开,不用就是不展开。

展开全部

#include<iostream>
namespace bear
{
	int hight = 188;
	int weight = 80;
}
using namespace bear;
int main()
{

	std::cout << hight << '\n' << weight << std::endl;
	return 0;
}

 部分展开

using bear::hight;

 与上面不同的时这里没有使用namespac这个,这里bear::就已经表示使用了空间域。

二、C++输出和输入

        C++用于输入输出的头文件include<iostream>,输出是std::cout,输入是std::cin,他们会自动识别类型,<<是流入运算符,>>流出运算符。可以一行输出输入(便利)如std::cout<<i<<d<<'\n';等于输出3个东西,等价于std::cout<<i;std::cout<<d;std::cout<<'\n';

	using namespace std;
    cout << "你好," << "欢迎来到" << "C++入门语法" << endl;
	//等效于
	printf("你好,欢迎来到C++入门语法\n");
#include<iostream>

using namespace std;
int main()
{
	int age = 0;
	cout << "多少岁了" << endl;
	cin >> age;
	cout <<"今年已经" << age <<"岁" << endl;//这里endl指的是结束标志
	return 0;
}

三、缺省参数

        缺省参数(缺省参数是声明或定义函数时为函数的参数指定一个缺省值),定义函数过程中可以不传参数,或者可以传参,这里函数里面定义要赋值,如果有传参就用传参如果没传参就会按函数中定义的默认。全缺省是指每个参数都有默认值,半缺省是指缺部分没有默认值。

using namespace std;
int Add(int a = 100, int b = 11)
{
	return a + b;
}
int main()
{
	int a = Add(10,20);//正常函数
	int b = Add(10);//半缺省参数
	int c = Add();//全缺省参数
	cout << a << "\n" << b << "\n" << c << endl;
	return 0;
}

在实参中:必须连续传参(从左到右依次传中间要么没参,后面也没参,如果有参会报错)

在形参中(函数中):也是必须连续填默认值(要么不填默认值,要么从右到左依次设置默认值,必须连续)。

缺省参数在创建多个项目时,给默认值是给到声明区域,也就是在头文件里面给默认值,不是多个项目时,那就可以放到定义中区。 

四、函数重载

        函数重载是C++同一作用域中出现相同函数名,这里解决的是出现相同函数名实习不同类型功能。要求同名函数的形参不同参数个数不同或者形参类型不同(ps:函数类型时,只要形参有一个不相同就能重载,当然这里形参中可能会有函数指针,即使该参数为函数指针然后类型不同,但是函数指针内的形参列表相同那就不能重载这里形参列表等价于形参类型指的就是函数参数个数,顺序和类型这三个。

#include<iostream>
using namespace std;

int Add(int a, double b)//原函数
{
	return a + b;
}
//缺省参数对应的重构函数(日常应该避免缺省和重构一起的,除非是类型明显不一样时可以使用)
int Add(int a = 10, int b = 10)
{
	return a + b;
}
//重构
double Add(double a, int b)//形参顺序不同
{
	return a + b;
}
double Add(double a, double b)//形参类型不同
{
	return a + b;
}
int Add(int a)//形参参数个数不同
{
	return a + 0;
}
int main()
{
	cout << Add(1, 2.0) << "\n" << Add(2, 5.5) <<"\n"<< Add(2.2,3.3)<< "\n" << Add(1) << endl;
	return 0;
}

以上是符合重构条件 

int Add(int a, double b)//原函数
{
	return a + b;
}
double Add(int a, double b)//只是返回类型不一样
{
	return a + b;
}

以上不符合重构条件 

void Func(int a = 10)//原函数
{
	//功能...
}
void Func()//重构函数
{
	//功能...
}
void Func(int a = 10)//原函数
{
	//功能...
}
void Func(double a =10)//重构函数
{
	//功能...
}
void Func(int a = 10,int b =20)//原函数
{
	//功能...
}
void Func(double a =10)//重构函数
{
	//功能...
}

缺省参数的出现可以导致函数使用时不用传参,但是这里缺省参数的函数和不需要形参的函数导致了调用函数时,编译器不知道该如何调用,从而导致报错。在使用重构函数时应该尽可能避免缺省参数的包含,如果实在需要,那就必须最多一个函数为缺省参数函数,其他必须全是为正常函数,为了可维护,建议使用时还是形参类型很明显不同时使用缺省和重构并合的程序。

五、引用

5.1介绍

        引用不是新定义⼀个变量,而是给已存在变量取了⼀个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同⼀块内存空间。既然是使用一个变量的别名,那就能对这个引用的变量做出改变并且能够改变原来变量(相当于本名叫熊大,别人取别名为熊,有人说熊去写代码了,也就是熊大去写代码了,别名做出的变化会影响本名)。

5.2引用的使用

        类型& 引用别名 = 引用对象;如int a =10;int& b = a;其中b的地址就是a所对应地址,b是a的一个小名,改变其中一个值,进而改变他原本大名(小名)的值,如int c = 20;b = c;那就是c的值赋予到b和a中去,但是b地址不会改变(引用不会改变指向,所以这里是把c的赋值修改b中)。

#include<iostream>

int main()
{
	int a = 10;
	int& b = a;
	int& c = b;
	int& d = c;
	std::cout << a << b << c << d << std::endl;//全是10
	d = 20;
	std::cout << a << b << c << d << std::endl;//全是20

	return 0;
}

5.3功能

        引用功能1:做函数形参,修改形参改变实参(归根就是把地址传过去了)对于二级指针也可以用引用如int*& a 来接受实参表示二级指针。

#include<iostream>

using namespace std;

void Spaw(int& change1, int& change2)
{
	int tmp = change1;
	change1 = change2;
	change2 = tmp;
}
int main()
{
	int a = 100;
	int b = 200;
	cout << "交换之前:" << a << " " << b << endl;//100 200
	Spaw(a, b);
	cout <<"交换之后:" << a << " " << b << endl;// 200 100
	return 0;
}
// ⼀些主要⽤C代码实现版本数据结构教材中,使⽤C++引⽤替代指针传参,⽬的是简化程序
void SeqPushBack(SLT& sl, int x)
{
    //功能实现...
}
typedef struct ListNode
{
	int val;
	struct ListNode* next;
}LTNode, * PNode;//这里分别表示typedef struct ListNode LTNode;和typedef struct ListNode* PNode;

// 指针变量也可以取别名,这⾥LTNode*& phead就是给指针变量取别名
// 这样就不需要⽤⼆级指针了,相对⽽⾔简化了程序
//void ListPushBack(LTNode** phead, int x)
//void ListPushBack(LTNode*& phead, int x)
void ListPushBack(PNode& phead, int x)
{
	PNode newnode = (PNode)malloc(sizeof(LTNode));
	newnode->val = x;
	newnode->next = NULL;
	if (phead == NULL)
	{
		phead = newnode;
	}
	else
	{
		//...
	}
}

int main()
{
	PNode plist = NULL;
	ListPushBack(plist, 1);
	return 0;
}

        引用功能2:引用做返回值类型,修改返回对象(也能减少拷贝,减小拷贝内存,提高效率)。

typedef struct SeqList
{
	int* arr;
	int capacity;
	int size;
}SLT;

//初始化
//用引用前
void SLTInit1(SLT* SL)
{
	SL->arr = NULL;
	SL->capacity = SL->size = 0;
}

//用引用后
void SLTInit2(SLT& SL)
{
	SL.arr = NULL;
	SL.capacity = SL.size = 0;
}

//用引用前
int* SLAt1(SLT* SL,int i)
{
	assert(SL->size);
	return &SL->arr[i];
}
//用引用后
int& SLAt2(SLT& SL, int i)
{
	assert(SL.size);
	return SL.arr[i];
}

在返回过程中其中函数内的变量会释放,但是会将返回的值拷贝下来放入编译器的临时变量(这个变量具有常性不可修改,以至于放回到主函数中不能通过函数调用直接修改),不过可以用引用取别名让他传地址进行修改,函数类型+&。  

//这样写是不对的,函数执行完了函数栈帧会销毁,内部变量全还给操作系统
//返回值会放入一个临时变量中,而他全名都销毁了,会导致和野指针一样
//对于后面调用的Func2被b覆盖掉
#include<iostream>
int& Func1()
{
	int a = 10;
	return a;
}
int& Func2()
{
	int b = 20000;
	return b;
}
using namespace std;
int main()
{
	int ret1 = Func1();
	cout << ret1 << endl;
	int ret2 = Func2();
	cout << ret2 << endl;
	cout << ret1 << endl;
	return 0;
}

在引用过程中,int&类型函数返回别名函数时,是需要程序之前有这个值在里面的,而不能,而不能是在int&类型函数中定义的变量(会返回随机值或者原值)。 

5.4特性

引用特性:1.引用必须初始化(int a =10;不能先int& r;再赋值r=a,而必须int& r = a;)

2.一个变量可以有多个引用(一个变量可以有多个别名)

3.引用一旦引用一个实体,不能再引用其他实体了(那个别名已经是别人的小名了,你不能把另一个人的别名/本名改为这个别名)

5.5const引用权限问题

        指针和引用会涉及到权限问题,而权限只能缩小不能放大(也就是你有权利只能做你权利范围内的事,不能越界权限)举例来说const int x = 10; 就不能int& a = x;因为a是x的别名,那就可以通过a来扩权。同理const int* x = 20;也不能int * b=x;扩权,只能说是降权限制可以,或者同级也行。归根结底就算const会产生一个临时变量,导致限制权限。

	const int a1 = 200;//常变量
	//int& a2 = a1;//越权(防止使用别名来修改a1)
	const int& a2 = a1;//同级
	int b1 = 300;
	int& b2 = b1;//同级
	const int& b3 = b1;//降级(限制权力)

 以上是引用权限

	const int a1 = 200;//常变量
	int* a2 = &a1;//越权

 在指针中也会出现这样的问题和引用一样会越权,只要是同级或者降级即可。

	double a = 2.0;
	int& b = a;//越权
    const int& b = a;//同级

解析: 在整形转换过程中都会出现这个临时变量,所以类似和const中设置的临时变量导致的。因为这个临时变量,使用引用时,那就会将a放入临时变量,然后再放入b中,这也就导致引用这个别名,在改变a时,它并不会改变b。这就是临时变量的用处,可以使得b引用的是临时变量的地址也就是临时变量的别名(深度理解临时变量对引用的作用原理)。

5.6与指针区别

引用和指针区别

1.引用不能改变指向,所以在链式结构中无法用引用替代他,还是得用指针(指针能不断改变指向对象)

2.引用必须初始化,而指针不必要初始化

3.删除空指针是无害的,而引用不能删除

4.指针很容易出现空指针和野指针的问题,引用会很少出现,一般会使用引用。

5.引用是取变量的别名,不用申请空间,而指针需要开辟空间。

6.引用在sizeof中也不同,引用大小指的是引用别人的大小(本质没空间开辟),而指针大小是32bit中4字节,64bit中8个字节。

六、内联函数

6.1介绍:传统宏函数优缺点引申内联函数概念

        对于c语言中宏函数的定义的缺点是不能调试/复杂/类型安全检查优点就是预处理阶段替代,不用建立函数栈帧,是一种提效。而针对这个缺点我们可以用内联函数inline修饰的函数叫做内联函数,编译时C++编译器会在调⽤的地⽅展开内联函数,这样调⽤内联函数就需要建⽴栈帧了,就可以提⾼效率来创建函数,在release中会直接替代,在debug中不会替代方便调试。

6.2内联函数的使用

        内联函数一般inline加在函数前面即可。

inline int Add(int a, int b)
{
	return a + b;
}

        在多项目中使用内联函数,如果是放入.h的内联函数,那就必须在.h内定义内联函数,而不能在.cpp中定义。当然如果是.cpp中某个地方使用,那就能在.cpp中直接定义。(内联函数会产生内部链接也就是预处理就处理好了,而不是地址。像一般函数产生的是声明然后定义它在预处理阶段不会还原回去,所以这里就是外部链接,会产生符号如下图要去F.o中寻找符号)这里只需要将内联函数和定义宏一样就好了,都是在预处理阶段就还原了,不需要寻找地址

在.h中

inline Add(int a,int b)
{
    return a+b;
}

 在test.cpp直接使用

#include<iostream>

using namespace std;
int main()
{
    int ret = Add(2,6);
    cout<<ret<<endl;
    return 0;
}

6.3内联函数缺点

        一般用于函数代码较小减小代码指令数目,对于代码量多的函数还是用定义函数创建函数栈帧合适,毕竟在预处理阶段,内联函数就把代码插入到调用中去了,如果调用次数过多会导致预处理将代码还原到调用中去,以至于代码指令变多,导致程序冗余(比如调用1w次函数而函数里面代码是100行,前提不是循环调用,那么内联函数质量是1w✖️ 100,而对于定义函数是1w+100)内。联函数中代码太多时,编译器会自动将内联改为函数栈帧函数。

七、nullptr

        nullptr是为了解决c++中NULL为0的情况c语言中NULL是(void*)0而C++指的是0,所以为了避免这个问题,这里用到nullptr来表示指针类型,而不是整数类型(不像在C++中的NULL表示0这个整数类型)

#include<iostream>

using namespace std;
void F(int x)
{
	cout << "int x" << endl;
}
void F(int* x)
{
	cout << "int* x" << endl;
}
int main()
{
	F(0);
	F(NULL);//c++中表示0
	F(nullptr);
	return 0;
}

 


网站公告

今日签到

点亮在社区的每一天
去签到