“指针常量引用” , int const*,const int* ,const int*& ,int* const&区别详解

“指针常量引用” , int const*,const int* ,const int*& ,int* const&区别详解_第1张图片


原本在处理函数模板特化的问题

template <class T>
bool IsEqual(const T& left,const T& right)
{
	return left == right;
}

这里我设计了一个模板来判断不同类型的值是否相等,

考虑到节省拷贝时间,同时又不想让函数修改实参,函数的形参设计为常量引用 const T&

在这里插入图片描述
ok,没什么问题。

但是如果面对字符数组,单纯的==只能比较指针的大小,完全考虑不到字符数组的内容,说明我们的模板覆盖的范围还不够。

“指针常量引用” , int const*,const int* ,const int*& ,int* const&区别详解_第2张图片

所以就字符数组的问题还需特化一个函数模板,其中使用strcmp函数,用来专门处理C字符串。

我想了下很简单嘛,把T换成char*,然后用strcmp来判断不就行了?

于是写出了字符数组函数模板的特化版本:

//针对字符串类型的特殊化处理--写一份函数模板的特化
template<>
bool IsEqual<char*>(const char*& left,const char* right)//const 修饰的是引用
{
	return strcmp(left,right)==0;
}

这里是对指针的常量引用,但是一写完我就觉得怪怪的,果然还没运行,编译器就出现了报错:

“指针常量引用” , int const*,const int* ,const int*& ,int* const&区别详解_第3张图片
一开始我以为是把函数模板特化的格式写错了,但是换了其他的类型之后,发现不是这里的问题。

于是我估计是指针的常量引用写错了,于是做了一小实验看一下,指针的常量引用到底是不是这么写的。

  • 第一步:先试下普通的指针引用,在这个函数里既可以修改指针指向的值(通过指针解引用),又可以修改指针本身(通过引用)
void print1(int*& p)
{
	cout << typeid(p).name() << endl;
}
int main()
{
	int* a;
	print1(a);
}

结果:一切正常。
“指针常量引用” , int const*,const int* ,const int*& ,int* const&区别详解_第4张图片

  • 第二步:按照模板特化的写法,改成指针的常量引用(言下之意就是说:我们不可以在函数中修改指针的指向)。
void print1(const int*& p)
{
	cout << typeid(p).name() << endl;
}
int main()
{
	int* a;
	print1(a);
}

果然,我找到了痛点!!

编译报错了:

error C2664:void print1(const int *&): 无法将参数 1 从“int *”转换为“const int *&

问题思考:为什么int*无法转换给const int*& ?

  • 第三步 先理解const int*int* const
int main()
{
	int b=10;
	int* pb=&b;
	
	int *const p0=pb;
	const int* p1=pb;
	return 0;
}

在常量指针的设计中,const的摆放位置很有讲究,

  1. const放在*前,是底层const,“锁”住的是指针指向的值,即该指针无法修改其指向的值。
  2. const放在*后,是顶层const,“锁”住的是指针本身,即该指针无法修改其自身的值,但是可以修改其自身指向的值。

p0 不能修改,但是 *p0 可以修改。

*p1不能修改,但是 p1可以修改。

  • 第四步 理解 const int*&int* const&(重点)
  1. const int*& 首先是一个普通引用,而且引用的类型是 const int*
  2. int* const& 则是常量引用,引用类型是int*

ok,类型分清楚了,看能不能区分下面p2~p5引用的初始化对错

int main()
{

	int b = 10;
	int* pb = &b;

	int* const p0 = pb;//“锁指针,不锁指向值”
	const int* p1 = pb;//“锁指向值,不锁指针”

	//区分正误
	int* const& p2 = p1;
	int* const& p3 = pb;
	int* const& p4 = p0;
	const int*& p5 = pb;
	
	
	return 0;
}
  1. p2的情况

    p1类型——const int*
    p2类型——int* const&

    p2是p1的常量引用,始终保证指向p1,而且无法用p2来修改p1的值,这里的值是指针本身。

    但是p2有权限可以修改指针指向的值,而p1又锁住了修改指针指向的值的权限,故权限被放大了!p1不会允许p2有这样的越权行为,故错误的初始化❌。
    “指针常量引用” , int const*,const int* ,const int*& ,int* const&区别详解_第5张图片

  2. p3的情况

    p3和p2的类型一致int* const&,这里改用pb初始化。pb的类型是int*

    pb的类型是int*,p3始终追随pb,而且p3(常量引用)无法修改pb的值。
    p3具有修改指向值的权限,pb谁都没锁柱,当然不会越权了。这里是正确的✔
    “指针常量引用” , int const*,const int* ,const int*& ,int* const&区别详解_第6张图片

  3. p4的情况

    p4依旧是int* const&类型,这里用类型为int *const的p0初始化。

    p0不能修改自身值,p4是常量引用也无法修改p0的值,恰如其分。

    p0可以通过指针修改指向值,p4也可以通过指针修改指向值,恰如其分。

    这样就是完全匹配的类型了,是正确的初始化✔。

    “指针常量引用” , int const*,const int* ,const int*& ,int* const&区别详解_第7张图片

  4. p5的情况

    p5类型——const int*&
    pb类型——int*

    这里就来到了我们的问题根本。

    p5紧紧跟随pb,p5只是普通引用,可以改变pb的指针值,这都ok。

    但是! p5指向的值是没有办法修改的,而pb有修改指针指向值的能力

    很显然,这样p5的const int*没有任何意义,调用p5的程序员设想的是:使用的p5指向的是一个无法改变的常量,但是这个“常量”却始终有可能被 *pb 胡乱变动,这不是瞎胡闹吗~

    引用变量"背刺"了,编译器表示我不允许!
    “指针常量引用” , int const*,const int* ,const int*& ,int* const&区别详解_第8张图片

正确处理:

类型完全匹配就可以正常使用引用了。

p1——const int*
p5——const int*&

“指针常量引用” , int const*,const int* ,const int*& ,int* const&区别详解_第9张图片

针对指针的引用:

  • 要么都不能对指向值修改
    锁指向值的指针,就用锁指向值的指针的普通引用锁住指向值的指针的常量引用
    const int*const int*&
    const int*const int* const &

  • 要么都能对指向值修改
    int*int* &
    int*int* const &

  • 注意⚠:
    锁指针值的指针,就必须使用常量引用。
    int const* 不能使用 int*& ,这里引用是可以修改锁住的指针的。需使用 int* const&


ok,回到我起初的函数模板特化,

“指针常量引用” , int const*,const int* ,const int*& ,int* const&区别详解_第10张图片
在这里插入图片描述
完美运行。

“指针常量引用” , int const*,const int* ,const int*& ,int* const&区别详解_第11张图片

你可能感兴趣的:(C++,c++,引用传递)