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

被折叠的 条评论
为什么被折叠?



