C++略识 之关键字const

2024-08-24 09:18
文章标签 c++ 关键字 const 略识

本文主要是介绍C++略识 之关键字const,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、const修饰的变量必须进行初始化

-->一般数据类型const常量初始化

const int i = 10; //合法

const int j; //非法,导致编译出错

-->指针const常量初始化

int *p = new int();

const int *p  =q; //等价于 int const *p = q;

-->引用const常量初始化

int *p = new int();

const int *p  =q; //等价于 int const *p = q;

[注意]:const常量有数据类型,而宏常量(#define)没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误。

2、const限定符声明的的变量具有只读属性

const int i = 10;

int j = 0;

// to do ...

i = j; //非法,导致编译出错

j = i; //合法

3、const相比较于define,可以避免不必要的内存分配

#defineSTRING "abcdefghijklmn\n" 

  const charstring[]="abcdefghijklm\n"; 

  ... 

 printf(STRING); //为STRING分配了第一次内存 

 printf(string); //为string一次性分配了内存,以后不再分配 

  ... 

 printf(STRING); //为STRING分配了第二次内存 

 printf(string); 

  ...

由于const定义常量从汇编的角度来看,只是给出了对应的内存地址, 而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。

4、是不是const的常量值一定不可以被修改呢?

非也!!!

const int i=0; 

int *p=(int*)&i; 

p=100;

通过强制类型转换,将地址赋给变量,再作修改即可以改变const常量值。说明了申明为常量的数据也可能被改变。

我这里补充的是不要对const 的滥用。强制绕过const 检查可能会引起运行错误。

把const int i=0 声明在函数内,能够达到你的目的;把const int i=0 声明为全局变量,虽然仍然能够用强制转换绕过编译器检查,但会引起运行错误。
可参考下例:

const int j=50;  
void main()  
{  const int i=0;  int *p=(int*)&i;  *p=100;   int *p2=(int *)&j;  *p2=200;  // runtime error  cout << &i << &j;  system("pause");  
}

5、必须分清楚是数值常量还是指针常量

int j=0; 

const int i=0; //i是常量,i的值不会被修改 

const int *p1=&i; //指针p1所指内容是常量,可以不初始化 

int * const p2=&j;//指针p2是常量,所指内容可修改 

const int * const p3=&i; //指针p3是常量,所指内容也是常量 

p1=&j; //不合法 左操作数包含“int *”类型 

*p2=100; //不合法  右操作数包含“int *const ”类型

[注意]:指向常量的指针并不能保证所指向的值不被改变。比如:

const int i=10;  
void main()  
{  const int j=20;  int k = 30;  const int * p1=&i;  const int * p2 = &j;  const int * p3 = &k;  // i=80; fail  // j= 20; fail  // *p3 = 50; fail  // 以上三种均未逃过编译器检查  k=80;   // succeed 逃过了编译器检查。 *p3 不行,但直接改k 允许。  system("pause");  
}

6、const修饰类的数据成员

class A  
{  const int size;  //to do…    
}

[注意]:const修饰的数据成员只在某个对象的生存周期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const修饰的数据成员的值可以不同。所以不能在类声明中初始化const数据成员,因为类的对象未被创建时,编译器不知道const 数据成员的值是什么。比如:

class A  
{  const int size = 100; //错误  int array[size]; //错误,未知的size  
}  

const数据成员的初始化只能在类的构造函数的初始化表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现。比如:

class A  
{  //to do…  enum { size1=100, size2 = 200 };  int array1[size1];  int array2[size2];    
}  

[注意]:枚举常量不会占用对象的存储空间,他们在编译时被全部求值。但是枚举常量的隐含数据类型是整数,其最大值有限,且不能表示浮点数。

7、const在函数声明中的应用

在一个函数声明中,const 可以修饰函数的返回值,或某个参数;对于成员函数,还可以修饰是整个函数,比如:

void fun1(const A *a );    
const A fun2( );
void fun3( ) const; // fun3( )为某个类的成员函数

[注意]:调用函数的时候,用相应的变量初始化const常量,则在函数体中,按照const所修饰的部分进行常量化,比如:

void fun1(const A *a); //不能对传递进来的指针的内容进行改变,保护了原指针所指向的内容
void fun2(const A &a); //不能对传递进来的引用对象进行改变,保护了原对象的属性<a target=_blank name="OLE_LINK2" style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></a>
<span style="font-family: Arial, Helvetica, sans-serif;  background-color: rgb(255, 255, 255);"><strong><span style="color: red;">[注意</span><span style="color: red;">]</span></strong></span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">:参数const通常用于参数为指针或引用的情况,且只能修饰输入参数;若输入参数采用“值传递”方式,由于函数将自动产生临时变量用于复制该参数,该参数本就不需要</span>
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">保护,所以不用const修饰。</span>

[小结]

--->对于非内部数据类型的输入参数,因该将“值传递”的方式改为“const引用传递”,目的是为了提高效率。例如,将void Func(A a)改为void Func(const A &a)。

--->对于内部数据类型的输入参数,不要将“值传递”的方式改为“const引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如void Func(int x)不应该改为void Func(const int&x)。






这篇关于C++略识 之关键字const的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1102069

相关文章

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给

2024/9/8 c++ smart

1.通过自己编写的class来实现unique_ptr指针的功能 #include <iostream> using namespace std; template<class T> class unique_ptr { public:         //无参构造函数         unique_ptr();         //有参构造函数         unique_ptr(