C++中对C进行了哪些扩展

本文详细介绍了C++中的命名空间如何避免变量冲突,const关键字在C与C++中的差异,以及C++中引用的概念和作用。此外,还探讨了C++中bool类型、三目运算符的增强以及类型系统的特点。通过对这些特性的理解,可以更好地掌握C++编程的精髓。

namespace

命名空间:在C语言多文件多模块开发中,难免会有相同的变量名引起冲突。C++中使用namespace对变量的作用域进行了划分,只在引用当前命名空间的情况下空间内的变量才生效。代码演示如下:

// 定义命名空间
namespace nameA
{
	int a = 10;
}

namespace nameB
{
	int a = 20;
}

// 使用命名空间
using namespace nameA;
int main()
{
	//using namespace nameB;  // 这时与nameA中的a变量冲突
	using namespace nameB; // 若非要引入nameB 下面访问时需要带名字空间名
	std::cout << "hello " << std::endl;
	using namespace std;
	cout << nameA::a << endl;
	cout << nameB::a << endl;

	return 0;
}

当然,命名空间也可以嵌套定义。

namespace nameB
{
	int a = 20;
	// 嵌套定义命名空间
	namespace nameC
	{
		struct Tescher {
			char name[20];
			int age;
		};
	}
}

int main()
{
	// 使用C空间时
	// nameB::nameC::变量
}

iostream

C++中iostream包含了标准输入/输出流对象。其中使用的cin和cout就是输入输出流对象

struct增强

#include <iostream>

using namespace std;

struct aaa {
	int a;
};

int main()
{
	aaa a; // C语言中必须写struct aaa才能完成定义。
			//这里在C语言编译中不通过的,而在C+中编译通过并可执行。
			// class类的定义也是如此
			// struct关键字与class关键字的功能相似,但有区别,区别后续了解。

}

const加强

	const int a = 0;  
	int const b = 0; // 这两种效果是一样的,都是修饰所指向的内存空间不能被修改

	int* const a1 = NULL;// a1的指向不能被修改,但可以修改a1空间的值。
	const int* b1 = NULL;// 不能指针所指向的空间内的值,但是可以修改指向
	const int* const c = NULL; // 指向和空间内的值都不能被修改
	// 原理 const总是修饰const右边部分的整体内容。

	// 以上是const基础认知

在C语言中,使用const修饰变量后使用指针还是可以修改该变量的值

// 	   const在C语言中好像起到了定义常量的作用,但是
	const int p = 10; // 定义一个常量
	// p = 20; //直接修改会报错。但是间接修改不会
	int* sp = (int*)&p;
	*sp = 20; // 此时p的值在C中已经变成了20,
	//所以说C语言中的const并没有严格意义上的将const修饰的变量当作常量来处理

而在C++中

	const int p = 10;
	int* sp = (int*)&p;
	*sp = 20;cout << p << endl;
	// 虽然通过指针修改了空间内的值编译通过了,但是打印出了还是10

	

问题来了,那我*sp操作了哪里
原因分析

C语言中const所修饰的变量 编译器会对该变量做标记,防止后面代码操作该变量
但是没有对内存空间进行保护操作。
而在C++中被const修饰的变量,编译器首先将它以键值对的形式放在符号表里
(不取地址并且作为全局变量不被引用时不单独开辟空间)
(所以C++中const常量可能分配空间也可能不分配存储空间)
若后续程序对p变量取地址操作时,
编译器单独再为这个变量开辟一个空间,供后续操作,所以不会报错。
此时*sp 的内存空间已经不是真正的 p 值的存放位置,
当程序再次调用p变量时,会在符号表中取出这个键值对的值。
而*sp改变的空间真实存在且对p变量不影响。
C++真正做到了将 p 常量化。
*/
cout << *sp << endl;// 成功打印20  以此来证明这个空间真实存在。

那么C++中const变量是如何开辟空间的

C++中const 在编译阶段分配内存

int x;
const int y = 0;// 发现后面有对它的取地址操作,在这里分配空间,
				// 而不是先给xz分配后遇到了取地址操作才开辟。
int z;

printf("%p\n%p\n%p\n", &x, &y, &z);

register增强

int main() {

	register int a = 0; // register关键字,请求寄存器存储变量

	printf("%x\n", &a); // 在C++中取寄存器地址编译不报错,但执行报错。
	// 在C中取寄存器地址编译报错。address of register variable ‘a’ requested

	for (int i = 0; i < 1000; ++i) {
		// C++在这种频繁使用i变量的时候会做优化
		// 优化方式:将i放在寄存器中执行,以此来提高速度。
		cout << i ;
	}
	return 0;
}

C++作为强类型语言,对类型要求更严格

#include <iostream>

using namespace std;

g(i) {
	printf("%d\n", i);
}
f() {
	return 5;
}
// 上述代码在C语言中可以打印,并且可以调用顺利执行,是因为C语言有默认类型
// 默认类型在C++中不成立。
// 而在C++中则不行。C++对类型的要求更为严格。无论是函数还是变量,必须有类型。

对三目运算符的增强

#include <iostream>

using namespace std;

int main()
{
	int a = 0;
	int b = 2;

	(a > b ? a : b) = 10; // 在C语言中,表达式不能作为左值。
						// C语言中,表达式返回值放在寄存器中的。
						// 除此之外,还说明C语言中表达式返回的是值
						// 而在C++中返回的是变量本身
						//变量的赋值都是通过地址赋值的,所以C语言中表达式不能作为左值

	cout << a << endl; // 打印10;
	// 在C++中 对三目运算符做了增强,他返回的变量本身 
	// C++编译器帮我们程序员做了取地址操作,使得三目运算返回的结果是这个值的地址
	// C++编译器把上述代码处理成了 *(a > b ? &a : &b) = 30
	// 所以C++中就三目运算符的返回可以作为左值



	return 0;
}

新增bool

#include <iostream>

using namespace std;

int main()
{
	bool a = true;  // C语言中不认识bool类型的,而在C++中增加了bool类型

	cout << "bool = " << sizeof(bool) << endl;// 一个字节
	cout << "a = " << a << endl;
	
	a = 0;
	cout << "a0 = " << a << endl; // 0 为假  a=0;

	a = 10;
	cout << "a10 = " << a << endl;// 有值为真,打印结果 a=1 bool类型只有0和1的值

	a = -1;
	cout << "a-1 = " << a << endl;// 负值为真  a=1;

	bool b1, b2, b3, b4; // 定义多个bool值有可能这些值总共只占1个字节,
						 // 这取决于编译器如何实现。
	cout << b1 << endl;  // 不初始化,编译不通过

	system("pause");
	return 0;
}

引用

	int a = 10;

	int& b = a; // 给a起了个别名b,操作b就相当于操作a

	cout << "a = " << a << endl;
	cout << "b = " << b << endl;

	b = 100;

	cout << "a = " << a << endl; // a被修改为100
	cout << "b = " << b << endl;

	// int& c; // 普通引用必须初始化,否则编译不通过

	cout << "&a = " << &a << endl; 
	cout << "&b = " << &b << endl;	// a和b的地址一样,两者是同一片内存空间的名字
	// 所以说,引用不开辟新的空间,但不是绝对不开辟。

下面看看引用类型开辟空间的情况

#include <iostream>

using namespace std;

struct A {
	int a;

	int& b;
	int& c;
};// 按照前面说的,引用类型不开辟空间,那么应该有 sizeof(A) == 4;

int main()
{
	// 但是
	cout << sizeof(A) << endl; // 打印12

	system("pause");
	return 0;
}

有人可能会想没有初始化,没有引用具体的空间,如果引用同一片空间肯定是不开辟空间的,如下:

#include <iostream>

using namespace std;

struct A {
	int a;

	int& b = a;
	int& c = a;
};// 按照前面说的,引用同一片空间,那么应该有 sizeof(A) == 4;

int main()
{
	// 但是
	cout << sizeof(A) << endl; // 打印12

	system("pause");
	return 0;
}

分析:
所以说C++中的引用,只是在逻辑上没有开辟空间,而在物理上开辟了空间。为什么呢?继续看

#include <iostream>

using namespace std;

// 函数的返回值是引用时
int& getAA() {
	int a = 10;
	return a;// 返回的是a引用的本身,也就是地址值
}

// 函数返回当左值时
int& gg() {
	static int a = 10;
	a++;
	cout << "a = " << a << endl;

	return a;
}

int main()
{
	int &a = getAA(); 
	int& b = getAA();

	//cout << a << endl;// 第一次正常打印10  因为编译器做了保存。

	//cout << a << endl;// 打印乱码,当栈空间释放后  找不到引用的空间

	//cout << b << endl;// 打印乱码

	gg() = 100;  // 先调用函数打印a = 11 ,然后返回a变量本身将函数中的a改为100。
	gg(); // 打印a = 101。
	
	return 0;
}

上述函数的引用,是否让大家想到const* 类型的指针
实际上,C++中的引用符号,就是 (类型 * const 变量 )c++编译器对&作为引用时,做了替换

引用的本质
引用在C++编译器中偷偷做了修改,int &b <==> int * const b
也就是说引用就是常量指针,不能修改指向的指针。

所以上述的结构体sizeof == 12;它实际上开辟了空间来存放变量的地址,以此作为常量指针,所以sizeof计算的时候,可以计算到它的大小。

优点
引用的功能,在C++中是非常好用的,它给用户的感觉就是起了个别名,在函数传参时使用引用类型,可以避免直接操作地址,C++编译器已经帮我们做好了一切指针工作。
函数形参使用引用如下:

// 复杂数据类型做引用
struct Teacher {
	char name[20];
	int age;
};

void print(Teacher& pt) { // 直接操作实参
	pt.age = 250;
}

int main()
{
	Teacher t1 = { 0 };

	print(t1); // pt 是 t1的别名,操作函数中的pt就相当于操作t1

	cout << "t1.age = " << t1.age << endl;

	system("pause");
	return 0;
}

所以说,引用用好了,可以大大美化代码。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值