1.C++—>命名空间

#include<iostream>
//命名空间
namespace bit
{
	int rand = 10;

	int Add(int x, int y)
	{
		return x + y;
	}
	struct Node
	{
		struct Node* next;
		int val;
	}ANode;
}


C/C++中,变量、函数和后⾯要学到的类都是⼤量存在的,这些变量、函数和类的名称将都存在于全 局作⽤域中,可能会导致很多冲突。使⽤命名空间的⽬的是对标识符的名称进⾏本地化,以避免命名 冲突或名字污染,namespace关键字的出现就是针对这种问题的。


1.1namespace的嵌套定义

namespace bit
{
	int rand = 10;
	namespace bit2
	{
		int rand = 100;
		int Addthenpow10(int x, int y)
		{
			return (x + y)*10;
		}
		struct Node
		{
			struct Node* next;
			int val;
		}BNode;
	}
	int Add(int x, int y)
	{
		return x + y;
	}
	struct Node
	{
		struct Node* next;
		int val;
	}ANode;
}

定义命名空间,需要使⽤到namespace关键字,后⾯跟命名空间的名字,然后接⼀对{}即可,{}中 即为命名空间的成员。命名空间中可以定义变量/函数/类型等。

主函数如下:

int main()
{
	printf("%p\n", rand);//全局变量中rand函数的地址
	printf("%d\n", rand());//调用rand函数生成随机数
	printf("%d\n", bit::rand);//::为域作用限定符
	printf("%d\n", bit::bit2::rand);//::为域作用限定符 命名空间的嵌套
	struct bit::bit2::Node node;

	bit::ST st2;
	bit::STInit(&st2, 10);
	bit::STPush(&st2, 1);
	bit::STPush(&st2, 2);
	std::cout << "hello cpp" << std::endl;
    //c++标准库都放在一个叫std(standard) 的命名空间中
		return 0;
}

1.2命名空间的使用

三种办法:

  1. 展开命名空间中全部成员,项目中不推荐,冲突风险很大,日常练习小程序为了方便推荐使用

  2. 指定命名空间访问,项⽬中推荐这种⽅式。

  3. using将命名空间中某个成员展开,项⽬中经常访问的不存在冲突的成员推荐这种⽅式。

using namespace std;
//↑全部展开
//↓部分展开某个成员
//using bit::STPush;
//↓指定命名空间访问
	bit::ST st2;
	bit::STInit(&st2, 10);

2.缺省参数

1.1 缺省参数

缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。在调⽤该函数时,如果没有指定实参 则采⽤该形参的缺省值,否则使⽤指定的实参,缺省参数分为全缺省和半缺省参数。(有些地⽅把 缺省参数也叫默认参数)

全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左 依次连续缺省,不能间隔跳跃给缺省值

函数声明与定义分离时,缺省函数不能在函数声明和定义中同时出现,规定必须函数声明给出缺省值。

//全缺省函数
void Func1(int a = 10, int b = 20, int c = 30)
{
	cout << "a = " << a << '\n';
	cout << "b = " << b << '\n';
	cout << "c = " << c << '\n' << endl;
}

//半缺省函数
void Func2(int a, int b = 10, int c = 20)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}

3.函数重载

c++与c不同 ,c++支持在同一作用域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同。这样c++函数调用就表现出了多态行为,使用更灵活。

//全缺省函数
void Func1(int a = 10, int b = 20, int c = 30)
{
	cout << "a = " << a << '\n';
	cout << "b = " << b << '\n';
	cout << "c = " << c << '\n' << endl;
}

//半缺省函数
void Func2(int a, int b = 10, int c = 20)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}

//参数类型不同
int Add(int left, int right)
{
	cout << "int Add(int left, int right)" << endl;
	return left + right;
}
double Add(double left, double right)
{
	cout << "double Add(double left, double right)" << endl;
	return left + right;
}

//参数类型顺序不同
void f(int a, char b)
{
	cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
	cout << "f(char b, int a)" << endl;
}

//返回值不同 不够重载 无法区分
//int f(int a, char b)
//{
//	cout << "f(int a,char b)" << endl;
//}
//void f(char b, int a)
//{
//	cout << "f(char b, int a)" << endl;
//}


//下面两个函数构成重载 
//但f1()在调用时 会报错 存在歧义 编译器不知道用谁

//参数个数不同
void  f1()
{
	cout << "f()" << endl;
}
void  f1(int a = 10)
{
	cout << "f()" << endl;
}


4.引用

4.1什么是引用

引⽤不是新定义⼀个变量,⽽是给已存在变量取了⼀个别名,编译器不会为引⽤变量开辟内存空间,
它和它引的变量共块内存空间。

写法 : 类型& 引用别名 =引用对象;

//引用 :b和c是a的别名
int a = 0;
int& b = a;
int& c = a;
//也可以给别名b取别名 d相当于a的别名
int& d = b;
++d;
//从调试可以看出地址都是一样的
cout << &a << '\n';
cout << &b << '\n';
cout << &c << '\n';
cout << &d << '\n';

4.2引用的特性

  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能引用其他实体
//引用:减少拷贝提高效率
void Swap(int& rx, int& ry)
{
	int tmp = rx;
	rx = ry;
	ry = tmp;
}
typedef struct SeqList
{
	int a[10];
	int size;
}SLT;
void SeqPushBack(SLT& sl, int x)
{
}
typedef struct ListNode
{
	int val;
	struct ListNode* next;
}LTNode, * 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()
{
	int x = 0, y = 1;
	cout << x << " " << y << endl;
	Swap(x, y);
	int* p1 = &x;//直接用在变量x前面是取地址,用在类型之后的是引用
	int*& rp1 = p1;//引用指针 (给指针取别名)
	SLT slt;
	SeqPushBack(slt, 1);
	//LTNode* plist = NULL;
	//ListPushBack(&plist, 1);
	LTNode* plist = NULL; 
	ListPushBack(plist, 1);

4.3const引用

可以引⽤⼀个const对象,但是必须⽤const引⽤。const引⽤也可以引⽤普通对象,因为对象的访 问权限在引⽤过程中可以缩⼩,但是不能放⼤。

所谓临时对象就是编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象, C++中把这个未命名对象叫做临时对象。

	//const引用
	const int b = 1;
	//int& ra =a //权限的放大×
	const int& rb = b;//正确 权限的平移

	int c = 3;
	const int& rc = c;
	//权限缩小

	int rra = a;//不涉及权限缩小放大 赋值拷贝 不是同一块空间
	const int rrb = b;
	const int* pb = &b;
	//int* ppb = pb;//权限的放大 错误

	int a = 10;
	const int& ra = a;
	const int& rra = 30;//可直接引用 仅限const
	//int& rra = 30;//普通引用不可以

	//int& rd = c * 3;
	//不能引用表达式 是权限的放大 有值存他的结果(临时变量/ 寄存器)
	//临时对象具有const属性
	const int& rd = c * 3;

4.4指针和引用的关系

C++中指针和引用在实践中他们相辅相成,功能有重叠性,但是各有自己的特点,互相不可替代。

  1. 语法概念上引用是一个变量的取别名不开空间,指针是存储一个变量地址,要开空间。
  2. 引用在定义时必须初始化,指针建议初始化,但是语法上不是必须的。
  3. 引用在初始化时引用一个对象后,就不能再引用其他对象;而指针可以在不断地改变指向对象。
  4. 引用可以直接访问指向对象,指针需要解引用才是访问指向对象。
  5. sizeof中含义不同,引用结果为引用类型的大小,但指针始终是地址空间所占字节个数 (32位平台下占4个字节,64位下是8byte)
  6. 指针很容易出现空指针和野指针的问题,引用很少出现,引用使用起来相对更安全一些

5.nullptr

C++ 中 NULL 可能被定义为字面常量 0,或者 C 中被定义为无类型指针 (void*) 的常量。不论采取何种定义,在使用空值的指针时,都不可避免地会遇到一些麻烦,本想通过 f(NULL) 调用指针版本的 f(int*) 函数,但是由于 NULL 被定义成 0,调用了 f(int x),因此与程序的初衷相悖。f((void*)NULL); 调用会报错。

C++11 中引入 nullptr,nullptr 是一个特殊的关键字,nullptr 是一种特殊类型的字面量,它可以转换成任意其他类型的指针类型。使用 nullptr 定义空指针可以避免类型转换的问题,因为 nullptr 只能被隐式地转换为指针类型,而不能被转换为整数类型。

在传统的C头文件(stddef.h),可以看到如下代码:

#ifndef NULL
	#ifdef __cplusplus
		#define NULL    //0
#else
		#define NULL    ((void*)0)
	#endif
#endif


//本想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,调用了f(int x),因此与程序的初衷相悖。
void f(int x)
{
	cout << "f(int x)" << endl;
}
void f(int* ptr)
{
	cout << "f(int* ptr)" << endl;
}

6.inline

  1. 用inline函数修饰的函数叫做内联函数,编译时c++编译器会在调用的地方展开内联函数,这样调用函数不用建立新的栈帧,起到提高效率的作用。
  2. vs编译器默认不展开inline,这样方便调试。
  3. inline相比于宏函数更简单且能够调试。
  4. inline不建议声明和定义分离到两个文件,这样会导致链接错误。因为inline被展开,就没有函数地址,链接时会报错。
//内联函数
//inline适用于频繁调用的小函数
//对于递归函数 ,代码相对多一些的函数,加上inline也会被编译器忽略(大约10行左右)
// 可以通过汇编观察程序是否展开
// 有call Add语句就是没有展开,没有就是展开了
#include "inline_fault.h"
inline int aandb(int x, int y)
{
	int ret = x + y;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	return ret;
}

致链接错误。因为inline被展开,就没有函数地址,链接时会报错。

//内联函数
//inline适用于频繁调用的小函数
//对于递归函数 ,代码相对多一些的函数,加上inline也会被编译器忽略(大约10行左右)
// 可以通过汇编观察程序是否展开
// 有call Add语句就是没有展开,没有就是展开了
#include "inline_fault.h"
inline int aandb(int x, int y)
{
	int ret = x + y;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	ret += 1;
	return ret;
}
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐