一.const
const很初学者看到这个东西都会很头疼,不知道究竟是什么东西,那么久简单地一句话总结一下,这句话 不一定对,只是方便记忆和理解。
1.const是什么究竟怎么看
const:const先作用于左边,若左边没有东西,就便会作用于右边。(重点)
我们来看几个例子
const int
int const //这以上两个就不多做解释,他们是一样的
const int* //const作用于右边,所以是指针指向的内容不可改变
int* const //const作用于左边,所以是指针不可改变,也可叫常量指针
const int* const //将上面两个综合一下
好的,新手肯定说哦我的头已经开始晕了,咱们就拿那句话就能得出结论(看注释)。就拿那简单的一句话来看,百分之99的const语句都不会出错,就不用看某某某老师,某某某教育机构的长篇大论,越听越迷糊。
2.顶层const与底层const
(1)如何看是顶层还是底层const
还是用我自己的话来说,它方便大家记忆和理解
只要const是修饰的对象或者类型本身,那么这个const就是顶层const。(重点)
我们还是举例来说明一下
const int* const
//左边的const修饰的是这个指针指向的类型并不是这个指针对象座椅它不是顶层const而是底层const
//右边的const修饰的是这个指针对象本身所以它是顶层const
//总体而言他是个既是底层有是顶层的const
const int
//这种const永远是顶层const
const int&
//因为& 其实就是指针的一种语法糖,所以这种const永远都是底层const,引用永远是没有顶层const
(2)有何作用
底层const会影响传入函数的对象,顶层const却不会影响,在函数传参时顶层const会被忽视掉的。 (重点)
我们还是举例来说明一下,毕竟看这干条条的话换谁都头疼
void Fun(const int a)
{
}
void Fun(int a)
{
}
//当我们生成时会发生以下报错
// error C2084: 函数“void Fun(const int)”已有主体
会发生这种错误,为什么呢?我们知道了 const int 是一个顶层const,所以这两个函数他们的传参类型其实是相同的,因为const在传参时会被忽略掉,这两个函数类型都是 void(int)这种函数类型所以,报错了。咱们再看一下这个例子。
void Fun(const int* a)
{
}
void Fun(int* a)
{
}
这回就可以完美生成没有报错,因为我们知道这个const是个底层const可以进行函数重载,如果我们把第一个修改成 void Fun(int* const a)那么他又会报错无法进行函数重载,因为他变成了修饰这个指针对象本身的顶层const,大家有兴趣可以试一试。
二:const的几个用处
const的用法和用处很多,尤其是函数重载,成员函数这里有很多疑惑的地方,我们来一一举例出const的用法。ps(上一部分已经举例了一个东东,但是我已经懒得排版了,大家不要忘了上面的东东)。
1.全局变量相关
大家都知道全局变量他是有静态存储区的,他在程序开始时分配空间,在程序结束时析构掉。不多说了,上例子吧。(这期博客确实有点无聊,都没有有趣的例子是吧)
#include<iostream>
extern int a = 1;//与int a=1;没有区别
int main()
{
std::cout << a << std::endl;
}
extren是干什么的,那我们这么写。
#include<iostream>
extern int a;
int main()
{
std::cout << a << std::endl;
}
这个a是没有定义的,他只是个声明,extern他是一个链接的一个关键字,相当于一个外部链接的作用,就是我们在同项目中另一个文件定义一个全局变量,这里将会打印出那处的值。我们来看。
我们可以清楚的看到打印出了100,这个就是extern的作用 。
但const的全局变量,它默认是内部链接,如果不知道什么是链接我可能也会出一篇博客来讲,总之期待吧,可能。那我们改一下会怎么样呢?
我们会获得到一个这样的报错: error LNK2001: 无法解析的外部符号 "int const a" (?a@@3HB)因为我们没办法获得到外部链接,不过再在左边的文件的const前加一个extern他就又可以正常输出了,这是const一个比较隐藏的小知识点。
2.const reference 延续临时变量生命周期
我们还是直接看例子吧
#include<iostream>
#include<string>
void* operator new(size_t size)
{
std::cout << "new" << std::endl;
return malloc(size);
}
void operator delete(void* m, size_t size)
{
std::cout << "delete" << std::endl;
free(m);
}
const std::string& Print(const std::string& s)
{
std::cout << s << std::endl;
std::cout << "I also alive" << std::endl;
return s;
}
int main()
{
Print("KNGG");
return 0;
}
它的输出结果是:
我们可以看到,临时变量的生命周期被延长了,到Print函数结束后才被“杀”掉,它有效的延长了临时变量的生命周期,要不然他该在传入后就被“杀”掉了。
3.类成员函数const重载的本质
别说了,上代码!
#include<iostream>
#include<string>
class vector
{
private:
int x, y;
public:
void Print();
void Print()const;
};
int main()
{
vector vec;
vec.Print();
return 0;
}
我们都知道在成员函数的结尾加上const可以进行重载,但实质上是怎么一回事呢,它为什么能重载?
实质上这种成员函数在编译后会先变为非成员函数,并且会有个name mangling的过程就是改名,在编译器解析之后咱们的成员函数就会边长以下模样:
void Print(vector* const this);//1
void Print(const vector* const this);//2
参考前面顶层底层const所以它可以重载。
我们写一个代码来当做课后作业吧(装起来了)也是成员函数的const重载
#include<iostream>
#include<vector>
class vector
{
public:
std::vector<int> ve;
public:
void Print(std::size_t size)const
{
std::cout << (*this)[size] << std::endl;
}
const int& operator [](std::size_t size)const
{
return ve[size];
}
int& operator[](std::size_t size)
{
return const_cast<int&>(const_cast<const vector&>(*this)[size]);
}
};
int main()
{
vector vec;
vec.ve = { 1,2,3,4,5,6 };
vec.Print(3);
return 0;
}
看能不能理解这一段代码,我过两天会把解析发在评论区中。
总结
ok以上就是const大概的内容了,当然还有很多东西没提到,也许也有错的地方还是那句话,欢迎大家来指正,谢谢大家看到这里。