命名空间和实体
概述
命名空间的作用:
多人合作完成项目时,有可能产生命名冲突,C++为了解决命名冲突,引入了命名空间。
所谓命名空间就是一个可以由用户自己定义的作用域,在不同的作用域中可以定义相同名字的变量,互不干扰,系统能够区分它们。
什么是命名空间:
程序员根据需要指定一些有名字的空间域,把一些全局实体分别存放到各个命名空间中,从而与其他全局实体分隔开。
C++中定义命名空间的基本格式
namespace wd{
int val1 = 0;
char val2;
}//end of namespace wd
在声明一个命名空间时,大括号内不仅可以存放变量,还可以存放以下类型:
变量、常量、函数、结构体、引用、类、对象、模板、命名空间等,它们都称为实体。
#include <iostream>
using std::cout;
using std::endl;
namespace wd{
//定义变量
int number = 10;
//定义常量
const int daysOfWeek = 7;
//定义函数
void m1(){
cout << "wd::m1" << endl;
}
}
命名空间的使用:作用域限定符、using编译指令
1.作用域限定符::
#include <iostream>
int main(){
std::cout<<"Hello,World!"<<std::endl;
}
优点:冲突产生的概率非常小
缺点:编写起来非常麻烦,如果代码中频繁使用,那么会非常繁琐。
2.using编译指令
using namespace std;//将std命名空间中的全部实体全部引入当前代码。
using编译指令尽量写在局部作用域。
当使用using编译指令,应避免命名空间实体与全局位置实体同名。
在不清楚命名空间中实体的具体情况时,尽量不使用using编译指令。
优点:非常简单,将std命名空间里面的实体全部引入。
缺点:会将除了需要的实体引入进来之外,还会引入未知的实体,可能产生冲突。
3.using声明机制:用什么声明什么。
using std::cout;
在同一作用域内用using声明的不同的命名空间的实体,不能是同名的,否则会发生冲突。
using声明机制的特点是:需要哪个实体的时候就引入到程序中,不需要的实体就不引入,尽可能减小犯错误的概率。
全局命名空间:全局命名空间是C++程序中默认的顶层命名空间,其中的 名称没有域限定,直接使用名称即可访问。它的作用域范围是整个程序。在所有函数外部定义的名字自动属于全局命名空间。
匿名命名空间:匿名命名空间是一个没有名字的命名空间,它用于封装不希望暴露给外部的实体,如函数、变量和类等。定义的实体只在当前文件中可见,不会被其他访问到,从而避免了与其他文件中相同名称的变量或函数发生冲突。
#include <iostream>
using std::cout;
using std::endl;
//全局命名空间
int number = 10;
//匿名命名空间
namespace{
int digit = 20;
}
当前流行的命名空间使用指导原则:
1.提倡在已命名的名称空间中定义变量,而不是直接定义外部全局变量或者静态全局变量。
2.如果开发了一个函数库或者类库,提倡将其放在一个命名空间中。
3.对于using声明,首先将其作用域设置为局部而不是全局。
4.不要在头文件中使用using编译指令,这样,使得可用名称模糊,容易出现二义性。
5.包含头文件的顺序可能会影响程序的行为,如果非要using编译指令,建议放在所有#include预编译指令后。
C++代码中头文件的引入顺序
1.自己自定义的头文件写在最顶端
2.C语言的头文件
3.C++的头文件
4.第三方库
const
const常量和宏定义常量的区别:
1.发生的时机不同:C语言中的宏定义发生时机在预处理时,做字符串的替换,如果出现了bug,则需要到运行阶段才可以发现;而const常量是在编译时(const常量本质还是一个变量,只是用const关键字限定之后,赋予只读属性,使用时依然是以变量的形式去使用)。
2.类型和安全检查不同:宏定义没有类型,不做任何类型检查;const常量有具体的类型,在编译期会执行类型检查。
3.在使用中,应尽量以const替换宏定义常量,可以减小犯错误的概率。
const修饰内置数据类型
void test (){
const int num = 30;
}
const修饰指针数据类型
Type *const pointer//指针本身是一个常量,不可以修改指针的指向,可以修改指针指向的内容。
const Type *pointer//指针指向的内容是一个常量,可以修改指针的指向,不可以修改指针指向的内容。
const在*的左边(const int * 和int const *),即为指向常量的指针,不能通过指针改变其指向的值,但是可以改变这个指针的指向。
const在*的右边(int * const),即为常量指针,不能改变这个指针的指向,但是可以通过指针改变其指向的值。
数组指针和指针数组:数组指针void (*ptr)[]指针数组void *array[]
函数指针和指针函数:函数指针ret (*ptr)(para1,para2)指针函数void *test(para1,para2)
在C++中,如果使用char *的话,强烈建议加上const修饰
申请/释放堆空间
C语言中的方式:malloc和free
C++中的方式:使用C++里面的new/delete表达式
new/delete表达式的使用方法:
#include <iostream>
//使用new/delete表达式来进行操作
void test3(){
//申请4字节的空间
int *pInt = new int();
*pInt = 20;
cout << *pInt << endl;
delete pInt;
pInt = nullptr;//安全地回收
}
//为数组开辟空间和释放
void test4(){
int *pInt = new int[5]();
delete []pInt;
pInt = nullptr;//安全回收
}
两者的比较:
1.malloc/free是C语言中的库函数;new/delete,是C++语言中的表达式;
2.new表达式的返回值是相应类型的指针,malloc返回值是void*
3.参数:malloc的参数是字节数,new表达式不需要传递字节数,会根据相应类型自动获取空间大小
4.初始化:malloc申请的空间不会进行初始化,获取到的空间是有脏数据的,new表达式申请空间时可以直接初始化
安全回收:在释放掉堆中的空间后,指针变量应置为nullptr。
引用
引用是什么:在C++中,在逻辑层面上(在使用时),引用是一个已定义变量的别名。
其语法:
int num = 10;
//此时&表示引用,并不是取地址
int & ref = num;
在使用引用的过程中,要注意以下几点:
1.&在这里不再是取地址符号,而是引用符号。
2.引用的类型需要和其绑定的变量的类型相同(学习继承后有所不同)
3.使用引用的同时,必须对引用进行初始化,否则编译时报错。
4.引用一经绑定,无法更改绑定。
引用的使用场景:
- 作为函数的参数:在没有引用之前,我们想通过形参改变实参的值,只有使用指针才能到达目的。但使用指针的过程中,不好操作,很容易犯错。而引用既然可以作为其他变量的别名而存在,那在很多场合下就可以用引用代替指针,因而也具有更好的可读性和实用性。这就是引用存在的意义。引用传递作为函数参数,会初始化引用,因为引用的底层是指针实现,也会有额外开销(比较小),如果函数参数是较大的对象或数据,那么使用引用作为函数参数可以避免复制实参,这样做可以减少开销。
- 作为函数的返回值:当以引用作为函数的返回值时,返回的变量其声明周期一定是要大于函数的生命周期的,即当函数执行完毕时,返回的变量还在。目的是避免复制,节省开销。
引用和指针的区别和联系:
联系:
1.引用和指针都有地址的概念,都是用来间接访问变量。指针指向一块内存,指针的内容是所指内存的地址;引用是某块内存的别名。
2.引用的底层还是指针来完成,可以把引用视为一个受限制的指针。
区别:
1.引用是一个别名,而指针是一个实体
2.引用使用时无需解引用,而指针使用时需要解引用
3.引用只能够在定义的时候初始化一次,之后不可变;指针可变。
4.sizeof引用得到的是所指向的变量的大小;而sizeof指
针得到的是指针本身的大小
5.指针和引用的自增运算含义不一样
引用的使用场景:
1.引用作为函数的参数:在没有引用之前,想通过形参改变实参的值,需要使用指针,但指针不好操作,容易犯错。有了引用之后,可以使用引用来代替指针,同时具有更好的可读性和实用性。
2.如果函数参数是较大的对象或数据,那么使用引用作为函数参数可以避免复制实参,可以减少开销。
使用常引用作为函数参数:
如果不希望函数体中通过引用改变传入的变量,那么可以使用常引用作为函数参数 - 不会修改值。
- 不会复制(不会造成不必要的开销)。
强制转换
C++中最常用的类型转换符static_cast
//目标类型 转换后的变量 = static_cast<目标类型>(要转换的变量)
int iNumber = 100;
float fNumber = 0;
fNumber = (float)iNumber;//C
fNumber = static_cast<float>(iNumber);
467

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



