-
2022-03-14 12:01:51
Const关键字
const 是constant 的缩写,本意是不变的,不易改变的意思。在 C++ 中是用来修饰内置类型变量,自定义对象,成员函数,返回值,函数参数。
1.用const修饰成员变量
- 普通变量
const 常量,只能赋值一次。
const int num = 111;//合法 num = 222;//报错,不能给常量赋值
- 指针变量
只有一个const时,如果const位于*的左侧,表示指针所指的数据是常量,不能通过该指针修改实际数据,指针本身是变量,可以指向其他内存单元。
int num1 = 123; int num2 = 456; const int * ptr_num1 = &num1; int const * ptr_num2 = &num1; ptr_num1 = &num2;//合法 *ptr_num1 = 789;//报错,不能给常量赋值
只有一个const时,如果const位于*的右侧,表示指针本身是常量,不能指向其他内存单元,指针所指的数据可以修改。
int num1 = 123; int* const ptr_num1 = &num1; *ptr_num1 = 456;//合法 ptr_num1 = &num2;//报错,不能给常量赋值
有两个const,位于*左右两侧,表示指针本身和指针所指向的数据都是常量,不能修改。
int num1 = 123; int num2 = 456; const int* const ptr_num = &num1; ptr_num = &num2;//报错,不能给常量赋值 *ptr_num = num2;//报错,不能给常量赋值
2.用const修饰函数的参数
如果参数是作输出用的,那么不论它是什么数据类型,也不论它采用“指针传递”还是“引用传递”,都不能加const 修饰,否则该参数将失去输出功能。所以const 只能修饰输入参数。
- 输入参数采用“指针传递”
加const 修饰可以防止意外地改动该指针,起到保护作用
void StringCopy(char* strA, const char* strB) { //指针对象strB可以修改 }
- 输入参数采用“值传递”
值传递,传递的是实际参数的一个副本,此时加const与修饰变量时的性质一致
//内置数据类型 void TestFun1(const int value) { value = 123;//报错,不能给常量赋值 } //非内置数据类型,A 为用户自定义的数据类型 void TestFun2(const A a) { a = A();//报错 }
由于函数将自动产生临时变量用于复制该参数,该输入参数无需保护,所以一般不加const 修饰。所以上述函数一般写作如下:
void TestFun1(int value); void TestFun2(A a);
- 输入参数采用“引用传递”
对于非内部数据类型的参数而言,例如:void TestFun2(A a) 这样声明的函数效率比较低。因为函数体内将产生A 类型的临时复制对象,而临时对象的构造、复制、析构过程都将消耗时间。为了提高效率,可以将函数声明改为void TestFun3(A& a),因为引用传递,对形参的操作等同于对实参的操作,即传递的不会是实参的副本,而就是实参,只是起了个别名而已。
void TestFun3(const A& a) { //此时无法修改a对象及其任何成员变量,也不能调用非const修饰的函数 }
3.用const修饰函数的返回值
- 返回值采用“指针传递”
那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。
const int * TestFun4(int& num) { return # } int num = 15; int * ptr_num = TestFun4(num);//报错,无法从“const int *”转换为“int *” const int * ptr_num = TestFun4(num);//正确使用方式
- 返回值采用“值传递”
由于函数会把返回值复制到外部临时的存储单元中,此时加const 修饰没有任何价值。
const int TestFun5(); const A TestFun6();//A 为用户自定义的数据类型 //所以上面的函数写法一般改写成下面的写法 int TestFun5(); A TestFun6();//A 为用户自定义的数据类型
- 返回值采用“引用传递”
如果返回值不是内部数据类型,那么为了提升效率,可以将函数A TestFun6() 改写为const A & TestFun6()。但是值得注意的是,此时一定要搞清楚函数究竟是想返回一个对象的“拷贝”还是仅返回“别名”就可以了。如果函数可以返回对象,也可以返回对象的引用,应该首选对象的引用,因为这样效率高。
const A & TestFun7(A & a) { return a; } //如果函数要返回局部对象,就应该直接返回这个对象,不应该采用返回对象的引用 const A & TestFun8()//错误使用方式 { A a; return a; }
4.用const修饰成员函数
任何不会修改数据成员的函数都应该声明为const 类型。
class TestClass { public: int value; int GetValue() const { return value; }//正确使用方式 void ModifyValue() const { value = 123;//报错,由于正在通过常量对象访问“value”,因此无法对其进行修改 } }
注意:const 关键字不能与 static 关键字同时使用,因为 static 关键字修饰静态成员函数,静态成员函数不含有 this 指针,即不能实例化,const 成员函数必须具体到某一实例。
使用mutable和volatile关键字修改的变量可以突破const限制。C++中的mutable和volatile更多相关内容 - 普通变量
-
C++之const关键字
2022-03-31 14:54:18const关键字在C++中真是无处不在,无论是函数参数,还是函数返回值,还是函数末尾都经常会看到const关键字,这表明C++中的const关键字是非常灵活的, 合理地使用const关键字能大大提高我们程序的健壮性。 被const...const作用
const关键字在C++中真是无处不在,无论是函数参数,还是函数返回值,还是函数末尾都经常会看到const关键字,这表明C++中的const关键字是非常灵活的,
合理地使用const关键字能大大提高我们程序的健壮性。被const修饰的即表明是常量性的、只读性的,不可随意修改的。因为const对象一旦创建后其值就不能再改变,所以const对象必须初始化。
const在C和C++中的区别
虽然在C中const也表示不可修改的意思,但是它的校验却没有C++中那么严格,比如在C中被const修改的变量我们可以通过间接绕过的方式修改,但是在C++中却是不行的,例如:
main.c int main() { const int a = 100; a = 50; // 编译错误 int *p = &a; *p = 50; // 编译成功,可以修改a的值 return 0; } main.cpp int main() { const int a = 100; a = 50; // 编译错误,a的值无法修改 int *p = &a; // 无法编译通过 const int *p = &a; // 可以编译通过 *p = 50; // 无法编译通过,不能通过指针修改a的值 return 0; }
const到底修饰谁
const在不同的位置修饰的内容不一样,对于const到底修饰的是谁,谁才是不可变的这个问题有一个简单的规则就是
const离谁近,谁就不能被修改;。
对于const修饰的变量从右往左看,const修饰谁就表示谁不可变 (注意const不能修饰)*。也就是const离变量名近就是用来修饰指针变量的,便是这个变量不可变,离变量名远就是用来修饰这个数据的,表示这个数据不可改变。我们通过下面的例子来分析下const到底修饰的是谁:
main.cpp int main() { int i = 10; const int *p1; // const距离int比较近,说明const修饰的是指针p1指向的数据不可变 *p1 = 100; // 错误 int const *p2; // 从右往左看,因为const不可以修饰星号,所以const修饰的是int,因此这种写法与p1是一致的 *p2 = 100; // 错误 int * const p3 = &i; // 这里const距离p3比较近,所以修饰的是p3变量,所以必须初始化 *p3 = 100; // 可以,虽然p3不可以变,但是其指向内容是可变的 int j = 20; p3 = &j; //错误,p3被const修饰,不可重新指向 const int * const p4 = &i; // 双重修饰,p4不可变,p4指向的内存也不可变 p4 = &j; // 错误 *p4 = 100; //错误 return 0; }
const与函数之间的那点事
先说下知识点:
1、const可以构成函数重载,如果const构成函数重载,const对象只能调用const函数,非const对象优先调用非const函数。
2、const放在函数末端const修饰类的成员函数表示在本函数内部不会修改类内的数据成员,不会调用其它非const成员函数
3、const函数只能调用const函数,const对象,只能调用const成员函数,非const函数可以调用const函数。
4、const修饰函数的返回值,如果该函数是以值的方式返回,则使用const修饰是没有意义的,如果该函数返回的是指针,则表示返回的指针的内容是不可修改的,需要使用const修饰来接收。下面是具体的例子:
using namespace std; class Person{ public: Person(int a):age(a),name(""){ } virtual ~Person(){ } int getAge() const{ name = "hell0"; // 错误,末端带const修饰,表示承诺在该函数内不会改变this对象的内容 std::cout << "getAge const" << std::endl; return age; } int getAge(){ // 与带const的构成函数重载 std::cout << "getAge" << std::endl; return age; } string getName(){ return name; } public: const int age; // 因为有const成员变量的存在,所以这个变量必须要初始化,所以需要自定义构造函数 string name; }; void printConst(const Person& person){ std::cout << "printConst" << std::endl; person.name = "hello"; // 错误,不可以修改person } void print(Person& person){ std::cout << "print const" << std::endl; person.name = "hello"; // 正确 } // 这里加的const是没有价值的 const Person getPerson1(){ return Person(1); } // 返回的指针内容不可修改,需要用const接收 const Person* getPerson2(){ return new Person(2); } int main() { Person person1 = getPerson1(); const Person *person2 = getPerson2(); // 因为getPerson2的返回值被const修饰,所以返回的指针的内容是不可修改的,所以不能去掉const person1.getAge(); // 调用不带const的getAge() person2->getAge(); // 调用带有const的getAge() print(*person2); // 错误const对象只能调用const函数 printConst(*person2); // 正确,const对象只能调用const函数 person1.getName(); // 可以 person2->getName(); // 错误,const对象只能调用const函数 return 0; }
相信结合代码注释和以上四点知识点应该还是挺好理解的,纸上得来终觉浅,要想真正掌握还是需要实打实地自己敲敲实践一把。
const与mutable
mutable可以说是与const为敌的一个关键字了,如果使用mutable关键字修饰类的成员变量,那么在那些被const修饰的函数体内,也是可以修改这个成员变量的,例如:
using namespace std; class Person { public: Person(int a) : age(a), name("") { } virtual ~Person() { } int getAge() const { name = "hell0"; // 可以,因为name被mutable修饰了 age = 20; // 不行,age没有被mutable修饰 std::cout << "getAge const" << std::endl; return age; } public: const int age; // 因为有const成员变量的存在,所以这个变量必须要初始化,所以需要自定义构造函数 mutable string name; }; void printConst(const Person &person) { std::cout << "printConst" << std::endl; person.name = "hello"; // 可以name被mutable修饰了 } int main() { Person person(1); person.getAge(); printConst(person); return 0; }
什么时候该用const
对于这个问题,笔者的意见是不管三七二十一,先把能用const修饰的地方都用上const,等等真正使用这些变量或者函数提示无法编译通过的时候再去详细斟酌是需要删除const修饰呢。还是自己的程序设计调用有问题。
相信很多C/C++都使用过宏,特别是一些C/C++高手更是把宏玩得出神入化,但是如果一个宏比较复杂,自己又对宏的掌握不是那么深入的话,往往会程序员们产生一些意想不到的困扰,在《Effective C++》一书的第第二和第三条款作者就提倡:
Item 2: 用 const, enums 和 inlines 取代 #defines
Item 3: 只要可能就用 const关注我,一起进步,人生不止coding!!!
-
const关键字用法
2022-04-19 16:15:44//类名 const 对象名[(实参列表)] Time const t1(12,34,46); 也可以把 const 写在最左边 //const 类名 对象名[(实参列表)] const Time t1(12,34,46); 在定义对象时指定对象为常对象,常对象必须要有初始值。 如果...一、常对象
1.先定义一个类
class Time { public: Time(int,int,int); //构造函数 void set_time(); void show_time(); private: int hour; int minute; int sec; }; Time::Time(int h,int m,int s) { hour = h; minute = m; sec = s; } void Time::set_time() { cin >> hour; cin >> minute; cin >> sec; } void Time::show_time() { cout <<hour<<":"<<minute<<":"<<sec<<endl; }
2.希望保证对象的数据成员不被改变,可以将这个对象声明为常对象,格式:
//类名 const 对象名[(实参列表)] Time const t1(12,34,46);
也可以把 const 写在最左边
//const 类名 对象名[(实参列表)] const Time t1(12,34,46);
1)在定义常对象时,常对象必须要有初始值。
2)如果一个对象被定义为常对象,则不能调用该对象的非 const 型的成员函数(除了系统自动调用的隐式构造函数、析构函数)
记忆:因为调用对象的非 const 型成员函数有可能改变对象里面的数据,而这是不被允许的(即使函数实际没有改变数据)。
例如以下使用非法:const Time t1(10,15,36); //定义常对象 t1.show_time(); //调用非 const 成员函数,非法
3)如果成员函数声明为 const ,就可以调用:
void show_time() const; //注意 const 在函数名和括号之后
这表示 show_time 是一个const 型函数,即常成员函数(常成员函数可以访问常对象中的数据成员,但是不允许修改常对象中数据成员的值)
4)如果常对象中的某个数据成员的值的确需要修改,加 mutable
比如想修改成员变量 hour 的值:mutable int hour;
这样就可以用声明为 const 的成员函数来修改 hour 的值。
二、常对象成员
可以将对象的成员声明为 const ,包括常数据成员和常成员函数。
1.常数据成员
用关键字 const 来声明常数据成员,其作用与用法跟一般常变量相似。
只能通过构造函数的参数初始化表对常数据成员进行初始化。
例如:class Time { public: Time(int,int,int); //构造函数 void set_time(); void show_time(); private: const int hour; int minute; int sec; };
只能通过构造函数的初始化列表对 hour 初始化
Time::Time(int h,int m,int s):hour(h),minute(m),sec(s) { }
2.常成员函数
常成员函数只能引用本类中的数据成员,而不能修改它们。
常成员函数可以引用 const数据成员,也可以引用 非const数据成员。
//注意 const 在函数名和括号之后void show_time() const; //注意 const 在函数名和括号之后
const是函数类型的一部分,在声明函数和定义函数时都要有 const关键字。
用法:
(1)如果在一个类中,有些数据成员的值允许改变,另一些数据成员的值不允许改变,则可以将一部分数据成员声明为const,以保证其值不被改变,可以用非const的成员函数引用这些数据成员的值,并修改非const数据成员的值。
(2)如果要求所有的数据成员的值都不允许改变,则可以将所有的数据成员声明为const,或将对象声明为const常对象,然后用const成员函数引用数据成员,这样起到双保险的作用,切实保证数据成员不被修改。
(3)如果已定义了一个常对象,只能调用其中的const成员函数,而不能调用非const成员函数(不论这些函数是否会修改对象中的数据)。这是为了保证数据的安全。如果需要访问对象中的数据成员,可将常对象中所有成员函数都声明为const成员函数,但应确保在函数中不修改对象中的数据成员。不要误认为常对象中的成员函数都是常成员函数。常对象只保证其数据成员是常数据成员。如果在常对象中的成员函数未加const声明,编译系统把它作为非const成员函数处理。
三、指向对象的常指针
将指针变量声明为const型,这样指针值始终保持为其初值,不能改变。
Time t1(10,12,15); Time t2(10,12,15); Time * const ptr; //const 在指针变量前面,规定 ptr 的值是常值 ptr = &t1; // ptr指向t1,此后不能再改变指向 ptr = &t2; // 错误,ptr不能再改变指向
定义指向对象的常指针格式:
类名 * const 指针变量名
也可以在定义指针变量时直接初始化:Time * const ptr = &t1; //定义指针变量时直接初始化
四、指向常对象的指针变量
为便于理解,先了解指向常变量的指针变量。
1.指向常变量的指针变量
1)定义一个指向常变量的指针变量 ptr
const char *ptr;
注意:const在最左边,它与类型名char紧连
2)如果一个变量已被声明为常变量,只能用指向常变量的指针变量指向它,而不能用普通的指针变量指向它。
例如:const char c[] = "boy"; const char *p1 = c; //合法 char *p2 = c; //非法
记忆:用指向非const变量的指针指向常变量后,正常是可以通过这个指针改变变量的值的,而变量是常变量,不允许改变,矛盾。
3)指向常变量的指针除了可以指向常变量,还可以指向普通变量,但是不能通过此指针改变该变量的值。
int a = 3; //定义了一个普通变量(非常变量) const int *p1; //定义了一个指向常变量的指针变量 p1 p1 = &a; //合法 *p1 = 5; //非法(不能通过 p1 改变变量 a 的值) a = 5; //合法
记忆:指向常变量的指针,指向普通变量后,这个指针也不会改变普通变量的值,不矛盾;
4)如果函数的形参是指向非 const 型变量的指针,例如:
void fun(int *ptr) //函数的形参指向非 const 型变量的指针 { qDebug("%d",*ptr); }
实参只能用指向非 const 变量的指针
int b = 88; int *p = &b; //指向非 const 变量的指针 fun(p);
记忆技巧:
①函数的形参是指向非 const 型变量的指针,表示函数里面可以通过这个指针改变这个变量的值。
②实参如果通过一个指针代入了一个变量,那么这个变量应该也允许改变。
运行结果:
③实参如果通过一个指针代入了常变量(使用指向常变量),那函数里面通过指针修改这个变量就非法了(不论函数实际有没有修改它),所以就出错了。
理解:
形参和实参指向同一变量。形参指向非const变量(变量值可通过指针改变),而实参指向const变量(变量值不允许通过指针改变),矛盾了。
因此C++要求实参用非const变量的地址(或指向非const变量的指针)。5)如果函数的形参是指向 const 型变量的指针,表示我函数里面绝对不会改变这个变量的值,这时候实参不论是不是const变量都可以
2.指向常对象的指针变量
指向常对象的指针变量用法跟上面指向常变量一样。
1)如果一个变量已被声明为常对象,只能用指向常对象的指针变量指向它,而不能用普通的指针变量指向它。
记忆:用指向非const对象的指针指向常对象后,正常是可以通过这个指针改变对象的值的,而对象是常对象,不允许改变,矛盾。
2) 如果定义了一个指向常对象的指针变量,并使它指向一个非 const 的对象,则其指向的对象是不能通过指针来改变的。
记忆:指向常对象的指针,指向普通对象后,这个指针也不会改变普通对象的值,不矛盾;Time t1(10,12,15); const Time *p = &t1; // 定义一个指向常对象的指针,指向了非const对象 t1.hour = 18; //合法,t1不是常对象 (*p).hour = 18; //不合法,p是指向常对象的指针
如果希望在任何情况下 t1 的值都不能改变,则应把它定义为 const型,如:
const Time t1(10,12,15);
- 指向常对象的指针最常用于函数的形参,目的是在保护形参指针所指向的对象,使它在函数执行过程中不被修改。如:
int main() { void fun(const Time *p); //形参是指向常对象的指针变量 Time t1(10,13,56); //定义Time类对象t1,它不是常对象 fun(&t1); //实参是对象t1的地址 return 0; } void fun(const Time *p) { p->hour = 18; //非法,p是指向常对象的指针 cout<<p->hour<<endl; }
4)如果定义了一个指向常对象的指针变量,是不能通过它改变所指向的对象的值,但是指针变量本身的值是可以改变的。如:
const Time *p = &t1; //定义指向常对象的指针变量p,并指向对象t1 p->hour = 10; //非法 p = &t2; //合法
五、对象的常引用
#include <iostream> using namespace std; class Time { public: Time(int,int,int); //构造函数 int hour; int minute; int sec; }; Time::Time(int h,int m,int s) { hour = h; minute = m; sec = s; } void fun(Time &t) //形参 t 是 Time 类对象的引用 { t.hour = 18; } int main() { Time t1(10,13,56); //t1是Time 类对象 fun(t1); cout<<t1.hour<<endl; //输出 t1.hour的值为18 return 0; }
如果不希望在函数中修改实参t1的值,可以把引用变量t声明为const(常引用),函数原型为:
void fun(const Time &t);
则在函数中不能改变 t 的值,也就是不能改变其对应的实参t1的值。
在C++面向对象程序设计中,经常使用常指针和常引用做函数参数:
① 这样既能保证数据安全,使数据不能随意修改
② 在调用函数时又不必建立实参的拷贝 -
const 关键字详解
2020-12-31 11:16:45const关键字的使用 可以节省空间,避免不必要的内存分配,因为编译器通常不为const常量分配内存空间,而是将它保存在符号表中,这样就没有了存储于读内存的操作,使效率也得以提高; c语言中被const修饰的变量虽然不...const 关键字详解
一、const关键字简介
const含义:只要一个变量前用const来修饰,就意味着该变量里的数据只能被访问,而不能被修改,也就是意味着“只读”(readonly)。
const规则: const在谁后面谁就不可以修改,const在最前面则将其后移一位;const修饰一个变量时,一定要给这个变量初始化,若不初始化,在后面也不能初始化。
const作用:
1:可以用来修饰变量,修饰函数参数,修饰函数返回值,且被const修饰的东西,都受到强制保护,可以预防其它代码无意识的进行修改,从而提高了程序的健壮性(是指系统对于规范要求以外的输入能够判断这个输入不符合规范要求,并能有合理的处理方式。ps:即所谓高手写的程序不容易死);
2:使编译器保护那些不希望被修改的参数,防止无意代码的修改,减少bug;
3:增强代码的可读性,给读代码的人传递有用的信息,声明一个参数,是为了告诉用户这个参数的应用目的。
二、const的用法
1.修饰局部变量、全局变量以及字符串常量
我们通过一段代码来解读(选自:const 修饰的全局变量、局部变量以及字符串常量)
代码如下(示例):#include<stdio.h> #include<stdlib.h> #include<string.h> const int a = 10; //const修饰的全局变量放在常量区 //1.const修饰的全局变量,即使语法通过,但是运行的时候会受到常量区的保护,段错误,运行失败 void test01() { //a = 100; //直接修改语法不通过 int *p = &a; *p = 100; //间接修改语法通过,运行时产生段错误 printf("a %d\n",a); } //2.const修饰的局部变量 void test02() { const int b = 10; //分配到栈上 //b = 100; //直接修改语法不通过 //c语言下称为伪常量 int *p = &b; *p = 100; printf("b %d\n",*p); //间接修改成功 //int a[b]; 伪常量是不可以初始化数组的 } //3.字符串常量 void test03() { char *p1 = "hello world"; char *p2 = "hello world"; char *p3 = "hello world"; printf("%s\n",p1); printf("%s\n", p2); printf("%s\n", p3); printf("%s\n",&"hello world"); //四个输出的结果一样 //p1[0] = 'z'; //不允许修改字符串常量 printf("p1[0] %c\n",p1[0]); //可以输出 } int main() { // test01(); // test02(); test03(); return 0; }
const修饰的普通变量:定义的时候就要给它赋初值,之后哪怕是赋相同值都不行。const修饰的局部变量还是变量,直接修改编译器报错,可以间接修改,存放在栈区,代码块结束时释放。
const修饰全局变量:直接修改编译器报错,间接修改编译器也许会通过,但运行时会报错(段错误)。const修饰的全局变量存放在全局(静态)存储区,编译期最初将其保存在符号表中,第一次使用时为其分配内存,在程序结束时释放。
注意:全局变量的作用域是整个文件,我们应该尽量避免使用全局变量,因为一旦有一个函数改变了全局变量的值,它也会影响到其他引用这个变量的函数,导致除了bug后很难发现,如果一定要用全局变量,我们应该尽量的使用const修饰符进行修饰,这样防止不必要的人为修改,使用的方法与局部变量是相同的。
const修饰字符串常量:字符串常量位于文字常量区(也有文章归类于代码区),本身就不允许被修改,如果没有const的修饰,我们可能会在后面有意无意的修改字符串常量,这样会导致对只读内存区域的赋值,然后程序会立刻异常终止。有了const,这个错误就能在程序被编译的时候就立即检查出来,这就是const的好处。让逻辑错误在编译期被发现。
注意:
const修饰的变量的存储位置在C语言和C++中是有区别,这个区别在第四节中介绍。2.修饰指针
常量指针、指针常量、指向常量的常指针
//第一种 const int *p1; //p本身不是const的,而p指向的变量是const //第二种 int const *p2; //p本身不是const的,而p指向的变量是const //第三种 int* const p3; //p本身是const的,而p指向的变量不是const //第四种 const int* const p4; //p本身是const的,而p指向的变量也是const
1.常量指针,就是上面代码中的第一和第二种,即指向常量的指针。
#include <stdio.h> int main() { int a = 5; int b = 20; const int *p = &a; // *p = 100; //编译器报错 p = &b; //完全可以 printf("%d\n",*p); //间接修改成功 return 0; }
需要注意的是以下两点:
①常量指针说的是不能通过这个指针改变变量的值,但是还是可以通过其他的方式来改变变量的值的。
②常量指针指向的值不能改变,但是这并不是意味着指针本身不能改变,常量指针可以指向其他的地址。2.指针常量,代码第三种。
#include <stdio.h> int main() { int a = 5; int b = 20; int *p = &a; int* const n = &a; // n = &b; //error: assignment of read-only variable ‘n’ *p = 8; printf("%d\n",a); return 0; }
指针常量是指指针本身是个常量,不能在指向其他的地址,需要注意的是,指针常量指向的地址不能改变,但是地址中保存的数值是可以改变的,可以通过其他指向该地址的指针来修改。
3.指向常量的常指针,代码第四种。
是以上两种的结合,指针指向的位置不能改变并且也不能通过这个指针改变变量的值,但是依然可以通过其他的普通指针改变变量的值。3.修饰函数的参数
const修饰参数是为了防止函数体内可能会修改参数原始对象。因此,有三种情况可讨论:
1.函数参数为值传递:值传递(pass-by-value)是传递一份参数的拷贝给函数,因此不论函数体代码如何运行,也只会修改拷贝而无法修改原始对象,这种情况不需要将参数声明为const。
例如:void func(int x)
不用写成void func(const int x)
2.函数参数为指针:指针传递(pass-by-pointer)只会进行浅拷贝,拷贝一份指针给函数,而不会拷贝一份原始对象。根据上面对指针常量、常量指针等讨论,同样分为三种情况:
①防止修改指针指向的内容
典型C库函数:char *strcpy(char *dest, const char *src);
②防止修改指针指向的地址
void swap ( int * const p1 , int * const p2 );
指针p1和指针p2指向的地址都不能修改。③防止修改指针指向的内容和地址
3.函数参数为引用(C++中):引用传递(pass-by-reference)有一个很重要的作用,由于引用就是对象的一个别名,因此不需要拷贝对象,减小了开销。这同时也导致可以通过修改引用直接修改原始对象(毕竟引用和原始对象其实是同一个东西),因此,大多数时候,推荐函数参数设置为pass-by-reference-to-const。给引用加上const,既可以减小拷贝开销,又可以防止修改底层所引用的对象。
4.修饰函数的返回值
1.如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const 修饰没有任何价值。例如把函数
int GetInt(void)
写成const int GetInt(void)
是没有意义的。2.如果给以“指针传递”方式的函数返回值加 const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。
const char * GetString(void); char *str = GetString(); //error: conflicting types for ‘str’ const char *str = GetString(); //这种用法才是正确的
3.但是返回值不是内部数据类型,例如:
A get_string(void)
,这样会产生一个临时的对象用来存放返回的数据,会调用拷贝构造函数,这样效率会低,所以采用“引用传递”A &get_string(void)
,如果加上const那么返回值的内容就不会被改变const A &get_string(void)
。这里对函数返回值使用 const 的目的在于限制不能将函数调用表达式作为左值使用。
例如有如下函数:int & min ( int &i, int &j);
可以对函数调用进行赋值,因为它返回的是左值:min ( a , b )=4;
但是,如果对函数的返回值限定为 const 的,即定义为:const int & min ( int & i, int &j );
那么,就不能对min ( a, b )
调用进行赋值了。事实上,函数返回值采用“引用传递”的场合并不多,这种方式一般只出现在类的赋值函数中,目的是为了实现链式表达。
例如:class A { A & operate = (const A & other); // 赋值函数 }; A a, b, c; // a, b, c 为A 的对象 a = b = c; // 正常的链式赋值 (a = b) = c; // 不正常的链式赋值,但合法
如果将赋值函数的返回值加const 修饰,那么该返回值的内容不允许被改动。上例中,语句
a = b = c
仍然正确,但是语句(a = b) = c
则是非法的。5.const成员函数(C++)
1.任何不会修改成员的函数都应该声明为const类型。
例如计数函数不会对成员进行修改所以int get_count(void)const;注意const成员函数的声明将const放到函数的尾部。
2.const成员函数不可以修改对象的数据。
const对象只能访问const成员函数,非const对象可以任意访问任意的成员函数。
3.class A { public: int get_count(int)const; }
int get_count(int)const
,准确的说这个const修饰的是this指向的对象,其实get_count(int)
这个函数在调用方法时会被编译器改写成get_count(A *const this, int)const
;为了不允许get_count()这个函数改变this指向的对象,则用const修饰这个函数,因此this指针的类型变为const A *const this。参考文章:
C语言中const关键字的用法
C++ const关键字的总结
const修饰函数参数,返回值,成员函数
三、const与define区别
关键字const用来定义常量,如果一个变量被const修饰,那么它的值就不能再被改变,我想一定有人有这样的疑问,C语言中不是有#define吗,干嘛还要用const呢,我想事物的存在一定有它自己的道理,所以说const的存在一定有它的合理性,与预编译指令相比,const修饰符有以下的优点:
1、预编译指令只是对值进行简单的替换,不能进行类型检查
2、可以保护被修饰的东西,防止意外修改,增强程序的健壮性
3、编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高
四、const在C语言中和C++中的区别
区别一:
C语言中const是定义了一个变量,是一个伪常量,该变量只具备读的功能;
C++中const是定义了一个常量,具备写的功能。const int a = 5; int array[a]; //在C语言中编译错误,因为a是一个变量 int array[a];//在C++中正确,因为a是一个常量
我们再来看一段相同的代码在C和C++中运行有什么不同(gcc\g++):
#include <stdio.h> int main() { const int a = 10; int *p = NULL; printf("%d\n", a); p = (int *) &a; *p = 20; printf("%d\n", a); }
C语言中编译运行结果:
10
20
C++中编译运行结果:
10
10
通过指针p间接修改a的值,如果在C语言编译器中,则打印20,常量a被修改,所以C语言中const是一个冒牌货;但是在c++中此处打印的结果依然是10,所以c++中的const才是真正意义上的常量。
在C语言中定义一个const变量a,它是一个伪常量,存放在栈区,可以通过指针间接修改;
在C++中定义一个const变量a,相当于定义了一个常量,这个变量将会被放到一个符号表中,没有给a分配空间,编译过程中若发现对const使用了extern或者&操作符,即当a在别的文件中被调用时或者被指针间接访问时才会分配空间,但是这个空间中的值和常量a是两回事,通过地址修改它不能真正的修改符号表中的常量a!区别二:
const修饰的变量在C语言和C++中存储位置不同。c语言中:
const全局变量存储在只读数据段,编译期最初将其保存在符号表中,第一次使用时为其分配内存,在程序结束时释放;
而const局部变量(局部变量就是在函数中定义的一个const变量,)存储在栈中,代码块结束时释放。
在c语言中可以通过指针对const局部变量进行修改,而不可以对const全局变量进行修改。因为const全局变量是存储在只读数据段。c++中:
在c++中是否要为const全局变量分配内存空间,取决于这个const变量的用途,如果是充当着一个值替换(即就是将一个变量名替换为一个值),那么就不分配内存空间,不过当对这个const全局变量取地址或者使用extern时,会分配内存,存储在只读数据段,也是不能修改的。
c++中对于局部的const变量要区别对待:
对于基础数据类型,也就是const int a = 10这种,编译器会把它放到符号表中,不分配内存,当对其取地址时,会分配内存,如果用一个变量初始化const变量,如const int a = b,那么也是会给a分配内存。
对于自定数据类型,比如类对象,那么也会分配内存。摘自文章:
const修饰的变量的存储位置区别三:
C语言中不能定义const函数;
C++中可以定义const函数,C++中的const成员函数:不能修改类的成员变量的值。#include <iostream> using namespace std; class Date { public: Date(int year = 2018, int month = 6, int day = 23) : _year(year) , _month(month) , _day(day) {} const int& GetDay()const //被const修饰后,this指针类型从Date* const this-->const Date* const this { return _day; } private: int _year; int _month; int _day; }; int main() { Date d1; cout << d1.GetDay() << endl; }
上面的Getday()方法中,如果确实想要修改某个成员变量的值,也不是不可以,可以在想要修改的成员变量前加mutable关键字。
(该段摘自:c中const和c++中const的区别)
五、const与static修饰变量的区别
static修饰的变量称为静态变量,存储在全局(静态)区,生命周期从程序编译到运行结束。
const修饰的变量并不会存放在全局(静态)区,而是取决于它定义的地方,局部定义的就存在栈区,全局定义的就存放在静态区。
可能有的人会疑惑:const修饰的变量不是不能修改的么,是只读的,那应该存在只读数据区啊。事实上并不是这样,C语言中,使用const修饰了一个变量,该变量不能直接修改,但是我们可以通过拿到这个变量的地址,然后通过它的地址来修改它。
(当然了,如果const修饰的是文字常量区的常量,如字符串常量,那么是无论如何也修改不了的) -
const关键字及其作用(用法),C语言const详解
2021-02-12 18:09:27它是定义只读变量的关键字,或者说 const 是定义常变量的关键字。说 const 定义的是变量,但又相当于常量;说它定义的是常量,但又有变量的属性,所以叫常变量。用 const 定义常变量的方法很简单,就在通常定义变量... -
const 关键字
2021-02-25 20:21:10const关键字修饰变量时,限制该变量的值不能够被改变。 由于const关键字修饰的变量的值不能够改变,所以const对象必须初始化; 相较同一类型的非常量变量,常量变量只能执行不改变其内容的操作,如算术运算、拷贝... -
C++ const 关键字详解(全网最全)
2021-05-02 16:02:311. const修饰符的作用 const类型定义: 指明变量或对象的值是不能被更新,引入目的是为了取代预编译指令 可以保护被修饰的东西,防止意外的修改,增强程序的健壮性 编译器通常不为普通const常量分配存储空间,而是将它们... -
JS高级-const关键字
2021-12-10 11:40:36const 是声明 常量 的 变量: 可以变化的量,顾名思义,声明后还可以对它进行修改 常量: 声明的数据不能修改的 const num2 = 10 num2 = 30 // 报错,因为常量值不能被修改 const也是认块级作用域 { ... -
const关键字的用法
2021-02-18 15:24:48目录一、常规用法二、const关键字修饰指针三、const关键字修饰函数形参四、const关键字与#define 一、常规用法 const关键字定义只读变量,被const定义的变量不允许给重新赋值。所以说它定义的是只读变量,这也就意味... -
C++里的const关键字的作用,详细的解释
2021-11-18 10:27:47Const 关键字 我觉得最好的解释是这个 ... const 变量 如果const变量不是外部(没有extern修饰)的,必须...这里特别强调类的成员函数,使用const关键字声明成员函数,说明该函数是一个“只读”函数,不会修改调用它的对象 -
【C++】const关键字的作用
2022-02-28 11:12:18const 修饰的成分会受到强制保护,以防意外的变动,能提高程序的健壮性,同时也有助于说明各个成分的特性,能提高代码的可读性。 -
const关键字
2022-02-07 19:25:20const关键字 经常会在程序中看到const关键字,const是constant的缩写,意为不变。 const在C++中可以用来修饰内置类型变量、自定义对象、成员函数、返回值、函数参数 1、const修饰普通类型变量 const int x = 7; int ... -
C语言中const关键字
2021-04-01 15:31:30C语言中const关键字是constant的缩写,通常翻译为常量、常数等,有些朋友一看到const关键字马上就想到了常量。事实上在C语言中const功能很强大,它可以修饰变量、数组、指针、函数参数等。本文将针对上述几种修饰... -
ES6—const关键字
2021-09-24 12:41:48const是ES6新增的关键字,作用是声明一个只读的常量。注意,常量标识符一般用大写字母表示。 const A = 100; A = 200; // 报错:Uncaught TypeError: Assignment to constant variable 使用const声明常量有... -
const关键字、变量、函数用const修饰
2022-03-27 22:54:37const关键字 如果把某个实体声明为const后,编译器会将其变成只读。 需要注意的是:const的对象必须要进行初始化,因为不能进行赋值操作,只能在定义的时候赋值。 好处:可提高程序的维护性 此时,不得不说就是define... -
C++中const关键字的用法总结
2019-09-24 09:43:34图粘贴不过来,看原文const 之前一直在学习C/C++,关于const的使用,这里出现一点,那里出现一点。...const关键字 常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的... -
C++ const关键字的作用
2021-04-25 21:15:27const形参可以接收const和非const类型的实参,例如 //i可以是int型或者constint型 voidfun(constint&i){ //... } 考虑类的情况: const成员变量:不能在类定义外部初始化,只能通过构造函数初始化列表进行... -
C语言const关键字的作用
2021-07-11 13:02:10欲阻止一个变量被改变,可使用const,在定义该const变量时,需先初始化,以后就没有机会改变他了。 const修饰一般变量的时候: int const number =2 或者 const int number = 2; const修饰的变量真的不能改吗?... -
const关键字的作用?
2021-02-01 13:44:45(1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了; (2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const... -
C++常对象精讲_const关键字的用法
2021-01-20 05:44:57const关键字: 用const修饰的定义对象称为常对象; 用const修饰的声明成员函数称为常成员函数; 用const修饰的声明数据成员称为常数据成员。 变量或对象被 const修饰后其值不能被更新。因此被const修饰的变量或对象... -
let与const关键字的区别
2021-09-23 16:56:29关键字let 1.let是ES6中新增的用于声明变量的关键字 *(1)let声明的变量只在所处于的块级有效【块级作用域是在{}内有效】 (2)在一个大括号中,使用let关键字声明的变量才具有块级元素,var声明的关键字不具备这个... -
const关键字的作用
2021-05-15 14:30:50const关键字1、 修饰变量2、 修饰数组3、 修饰指针4、 修饰函数参数 1、 修饰变量 C语言中采用const修饰变量,功能是对变量声明为只读特性,并保护变量值以防被修改。 举例说明如下: const int i = 5; 上面这个... -
const关键字及其作用(用法)
2021-01-26 10:57:23它是定义只读变量的关键字,或者说 const 是定义常变量的关键字。 说 const 定义的是变量,但又相当于常量;说它定义的是常量,但又有变量的属性,所以叫常变量。用 const 定义常变量的方法很简单,就在通常定义变量... -
java判断题1.在Java的方法中定义一个常量要用const关键字。
2021-03-01 07:29:122.抽象方法必须在抽象类中,所以抽象类中的方法都必须是抽象方法。3.用final修饰的变量叫常量。4.描述对象的两个要素是属性和方法。5.接口是特殊的类,所以接口也可以继承,子接口...2.抽象方法必须在抽象类中... -
C++引用和const关键字
2022-02-06 13:50:48// 输出5 常引用 定义引用时,前面加const关键字,即为“常引用”。 int n; const int & r = n; // r的类型是const int & 不能通过常引用去修改其引用的内容: 并不是说引用的内容不能被修改 int n = 100; const ... -
C语言const关键字详解
2020-04-06 02:02:45它是定义只读变量的关键字,或者说 const 是定义常变量的关键字。 说 const 定义的是变量,但又相当于常量;说它定义的是常量,但又有变量的属性,所以叫常变量。用 const 定义常变量的方法很简单,就在通常定义变量... -
c++中const关键字
2020-05-06 23:48:00在C++中const关键字可以修饰类的成员变量,成员函数和修饰类对象 1、const修饰成员变量:在类中修饰成员变量,可以是private也可以是public。 此时修饰的变量不能在其他函数中被修改,也就是被赋值。而且这个const... -
【TypeScript】使用 const 关键字声明只读变量
2021-12-20 09:15:43关键字 let 并不是声明变量的唯一新方法。 在 ES6 中,你还可以使用 const 关键字声明变量。 const 具有 let 的所有出色功能,另外还有一个额外的好处,即使用 const ...