C/C++中const关键字用法总结

news2024/11/26 1:23:26

C++基础专栏:http://t.csdnimg.cn/4FdOH

目录

1.引言

2.修饰变量

2.1.修饰普通变量

2.2.修饰指针

2.3.修饰对象

3.修饰函数形参

4.修饰函数返回值

5.修饰类成员函数

6.const与constexpr区别

7.总结


1.引言

        在面试的过程,面试官经常都会问到const关键字有哪些用法?在C/C++中各有些什么作用?今天就在这里我们好好的总结一下const的全方位用法。

2.修饰变量

2.1.修饰普通变量

        在普通变量前面加上const,表示该变量是常量,不能随便更改值的变量。const 类型的变量必须在定义时进行初始化,之后不能对const型的变量赋值。示例如下:

在变量定义前加关键字const,修饰该变量为常量,不可修改
const int year= 12; 
const char p = 'f';

注意:在函数内部定义的const局部变量和全局定义的const变量还是有很大的不同的?函数内部定义的局部const变量存放在栈区中,会分配内存(也就是说可以通过地址间接修改变量的值), 如:

void func()
{
    const int a = 10;
    //a += 1; //编译错误,常量是不能修改的
    int* p = &a;
    *p = 20; //a = 20, 通过指针间接是可以修改a的值的
}

全局const变量存放在只读数据段(不能通过地址修改,会发生写入错误), 默认为外部联编,可以给其他源文件使用(需要用extern关键字修饰)。如:

const int  a = 10;
//const int b = a; //不能用全局变量全局初始化const变量,编译不通过,表达式必须含有常量值

void func()
{
    int* p = &a;
    *p = 20;//编译通过,但运行时会发生写入错误
}

如果是在C++类中定义的const成员变量,那么必须在初始化列表中赋值,如:

//类定义
class CBase
{
public:
    explicit CBase();

private:
    const int m_a;
};

//类构造函数
CBase::CBase()
    : m_a(100)
{
    //m_a = 200; //在这里赋初值会出现编译错误,因为这里不是m_a不是赋初值的地方
}

2.2.修饰指针

const修饰指针有几种情况:

① const修饰指针 --- 常量指针

语法:const 数据类型 * 变量名;如:const int* p;

② const修饰常量 --- 指针常量

语法:数据类型 * const 变量名;如:int* cont  p;

③ const即修饰指针,又修饰常量

语法:const 数据类型 * const 变量名;如:const int* const p;

综合示例如下:

int func() {
	int a = 10;
	int b = 10;

	//1)常量指针:const修饰的是指针,指针指向可以改,指针指向的值不可以更改
	const int * p1 = &a; 
	p1 = &b; //正确
	//*p1 = 100;  报错

	//2)指针常量:const修饰的是常量,指针指向不可以改,指针指向的值可以更改
	int * const p2 = &a;
	//p2 = &b; //错误
	*p2 = 100; //正确

    //3)const既修饰指针又修饰常量
	const int * const p3 = &a;
	//p3 = &b; //错误
	//*p3 = 100; //错误

	return 0;
}

技巧:看const右侧紧跟着的是指针还是常量, 是指针就是常量指针,是常量就是指针常量

2.3.修饰对象

        const修饰类对象表示该对象为常量对象,其中的任何成员都不能被修改。对于对象指针和对象引用也是一样。
        const修饰的对象,该对象的任何非const成员函数都不能被调用,因为任何非const成员函数会有修改成员变量的企图。

        示例如下:

class CMyTest
{
public:
    CMyTest() : m_a2(10),m_b2(20) {}
    void func1(){
        m_b1 = 10; //OK
        //m_b2 = 30; //ERROR: m_b2是常量不能修改
        m_a3 = 40; //OK
    }

    void func2() const{
        //m_b1 = 10; //ERROR: const成员函数不能修改非const成员变量
        //m_b2 = 60; //ERROR: m_b2是常量不能修改
        m_a3 = 80; //OK
    }

public:
    int m_a1;
    const int m_a2;
    mutable int m_a3;

private:
    int m_b1;
    const int m_b2;
    mutable int m_b3;  
};

int main()
{
    const CMyTest test;
    
    //test.m_a1 = 1000; //ERROR : 编译错误,常对象不能非const成员变量
    //test.m_a2 =  2000; ; //ERROR : 编译错误,常对象不能修改const成员变量
    test.m_a3 =  3000; ; //OK,常对象能修改mutable成员变量

    return 0;
}

        

3.修饰函数形参

在函数参数前面加上const,表示在函数内部不能修改该参数的值,用const来防止误操作。如:

struct student {int num};

void func(const student &stu){
    stu.num = 56;  //这句就错误了,不能修改stu中的内容
}

student abc;
abc.num = 100;
func(abc);
std::cout << abc.num << std::endl; //100

4.修饰函数返回值

返回基本数据类型的常量值:当函数返回基本数据类型(如 intfloat 等)时,使用 const 修饰返回值通常没有太大意义。因为基本类型的返回值通常是按值返回,即返回的是一个副本。修改这个副本并不会影响原始值。如:

const int getValue() {
    return 11000;
}

返回对象或复合类型的常量引用:当函数返回对象或复合类型(如自定义类、结构体等)时,使用 const 修饰返回值可以防止返回的对象或复合类型被修改。这是一种常见且有用的做法,特别是在返回类的成员变量时。如:

const MyClass& getClass() const {
    return myObject;
}

getClass 函数返回 MyClass 类型对象的常量引用。这意味着调用者可以读取返回的对象,但不能修改它。这种做法既保证了数据的安全性,又避免了不必要的复制。

class MyClass {
private:
    int value;

public:
    const int& getValue() const {
        return value;
    }
};
// 在使用时
MyClass obj;
int a = obj.getValue(); // a 不是 const
// obj.getValue() = 10; // 错误,不能通过 const 引用修改值

getValue 方法返回 value 成员的常量引用,这样就保护了内部数据不被外部修改。

5.修饰类成员函数

        成员函数后加const后我们称为这个函数为常函数;const成员函数,能够访问所有成员变量,但是在函数体内不能直接修改变量的值(包括普通成员变量),如果需要在函数体内修改普通成员变量的值,需要在变量定义的前面添加mutable关键字,或者通过地址间接修改

        注意:const成员函数只能被该类的const对象访问。 

示例如下:

class CMyTest
{
public:
    void func() const{
        //a = 40; //没有加mutable修饰的变量在const成员函数内不能直接修改
        //间接修改a的值
        int* p = const_cast<int*>(a);    
        *p = 50;
        //用mutable修饰的变量直接通过
        c = 60;
    }

private:
    int a = 10;
    const int b = 20;
    mutable int c = 30;
};

6.const与constexpr区别

constexpr 和 const 都是 C++ 中用于声明常量的关键字,但它们在使用上有一些重要的区别:

const

  • const 用于声明一个常量,表示变量的值不可修改。
  • const 可以用于各种类型的变量,包括基本数据类型、对象、指针等。
  • const 变量的值可以在运行时确定。这意味着它可以被初始化为在编译时未知的值,比如函数返回值或者用户输入。
  • const 更加通用,适用于任何需要防止修改的场景。

constexpr

  • constexpr 用于声明表达式为编译时常量,意味着它的值必须在编译时就已知。
  • constexpr 常量可以用在需要编译时常量表达式的上下文中,如数组大小、整数模板参数等。
  • constexpr 函数能在编译时对其输入进行计算,只要所有输入也都是编译时已知的常量。
  • 使用 constexpr 声明的变量或函数表示你希望编译器验证它们能够在编译时求值。
  • constexpr 要求其初始化表达式必须是一个编译时常量表达式。

主要区别

  • 编译时 vs. 运行时constexpr 确保变量或函数的值能在编译时被确定,而 const 变量的值可以在运行时确定。
  • 使用场景constexpr 适用于需要在编译时进行计算的场景,比如作为模板参数或数组大小。const 更适用于程序运行中不需要修改的值。

示例如下:

//使用const 
const int max_size = getSizeFromUser(); // 运行时获取大小
const double pi = 3.14159;

//使用constexpr
constexpr int max_array_size = 100; // 编译时已知的数组大小
constexpr double computeArea(double radius) {
    return 3.14159 * radius * radius; // 编译时计算面积的函数
} 

7.总结

使用const还有很多好处:

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

2) 可以节省空间,避免不必要的内存分配: const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象宏一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而宏定义的常量在内存中有若干个拷贝。

3) 优化性能:对于基本数据类型,const对象可能会被编译器优化,因为它们的值在程序的执行过程中不会改变。const方法和constexpr函数可以在编译时进行计算和优化,这可以提高程序的运行效率。

4) 增加代码的可维护性:通过将某些变量或方法标记为const,你可以更容易地重构和修改代码,同时保持对原始设计意图的忠实。const正确性(const-correctness)是一种良好的编程实践,它有助于减少错误并增加代码的稳定性。

5) 为函数重载提供了一个参考:const修饰的函数可以看作是对同名函数的重载。如:

clas MyTest
{
public:
    void func(int a, int b);
    void func(int a, int b) const;
};

6) 接口设计:在设计类和函数接口时,使用const可以明确哪些操作是安全的,哪些操作可能会修改对象状态。这对于库的设计尤其重要,因为它允许库的使用者更加清晰地理解如何与库进行交互。

7) 保护数据:在多线程环境中,将数据声明为const可以确保它不会被多个线程同时修改,从而避免数据竞争和未定义行为。当然,在多线程环境中仅仅使用const是不够的,但它是一个有用的工具。

8) 兼容性和转换:const对象可以安全地转换为非const对象(在不需要修改对象的情况下),这提供了更大的灵活性。

参考:

const (C++) | Microsoft Learn

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1580257.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

arm-linux-gnueabihf-gcc默认目录

默认编译的头文件目录&#xff1a; /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/lib 默认编译的库文件目录&#xff1a; /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/include/ …

[C++]哈希应用之位图布隆过滤器

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 主厨&#xff1a;邪王真眼 主厨的主页&#xff1a;Chef‘s blog 所属专栏&#xff1a;c大冒险 总有光环在陨落&#xff0c;总有新星在闪烁 前言&#xff1a; 我们之前…

Hadoop安装部署-NameNode高可用版

Hadoop分布式文件系统支持NameNode的高可用性&#xff0c;本文主要描述NameNode多节点高可用性的安装部署。 如上所示&#xff0c;Hadoop分布式文件系统部署了NameNode的Master主节点以及NameNode的Slave副节点&#xff0c;当Master主节点发生故障变得不可用时&#xff0c;ZooK…

Vulnhub:DEVCONTAINER: 1

目录 信息收集 arp nmap nikto whatweb WEB 信息收集 dirmap 文件上传 提权 系统信息收集 横向提权 信息泄露 get root 信息收集 arp ┌──(root㉿ru)-[~/kali/vulnhub] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:50:56:2f:dd…

Windows Server 2008添加Web服务器(IIS)、WebDAV服务、网络负载均衡

一、Windows Server 2008添加Web服务器&#xff08;IIS&#xff09; &#xff08;1&#xff09;添加角色&#xff0c;搭建web服务器&#xff08;IIS&#xff09; &#xff08;2&#xff09;添加网站&#xff0c;关闭默认网页&#xff0c;添加默认文档 在客户端浏览器输入服务器…

无需训练,这个新方法实现了生成图像尺寸、分辨率自由

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了免费的人工智能中文站https://ai.weoknow.com 新建了收费的人工智能中文站https://ai.hzytsoft.cn/ 更多资源欢迎关注 近日&#xff0c;来自香港中文大学 - 商汤科技联合实验室等机构的研究者们提出了FouriScale&…

蓝桥杯-外卖店优先级

代码及其解析 #include<bits/stdc.h> using namespace std; const int N100010;int order[N]; //order[id] 第id号店上一次的订单,记录的是时间 int prior[N]; //prior[id] 第id号店的优先级 int flag[N]; //flag[id] 第id号店在不在优先缓存中struct node{int…

有关栈的算法

例题一 解法&#xff08;栈&#xff09;&#xff1a; 算法思路&#xff1a; 本题极像我们玩过的「开⼼消消乐」游戏&#xff0c;仔细观察消除过程&#xff0c;可以发现本题与我们之前做过的「括号匹配」问题是类似的。当前元素是否被消除&#xff0c;需要知道上⼀个元素的信息…

YOLOv8分类识别训练配置详细

目标&#xff1a; 实现自己的图像分类算法模型训练&#xff0c;应用。 掌握数据集配置方式&#xff1b; 掌握训练、预测命令语句 掌握训练结果分析 学习内容&#xff1a; 1.图像分类数据集配置方式 例如&#xff1a; 制作一个表情分类数据集。 # 0 - 6 文件夹分别label为&…

notion的使用心得

从老石的视频知道了notion是一个很强大的管理工具&#xff1a;这就是最棒的效率软件&#xff01;如果不是&#xff0c;我倒想试试你的 | Notion使用技巧分享_哔哩哔哩_bilibili 我一时半会不能全部学会&#xff0c;但是借用大家的好模板&#xff1a;如何用5分钟搭建简洁高效的…

ideaSSM 网上选课管理系统bootstrap开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 idea 开发 SSM 网上选课管理系统是一套完善的信息管理系统&#xff0c;结合SSM框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff…

在短视频的挤压下,长视频何去何从?长视频行业能否借助AI重拾信心?

回顾2019年中国网络视听大会&#xff0c;长视频行业的领袖们曾在演讲中对短视频进行了猛烈抨击&#xff0c;这一场面至今仍然令人印象深刻。当时&#xff0c;长视频平台似乎还有一些自信&#xff0c;但如今&#xff0c;他们已经被来自各方的竞争对手挤得喘不过气来。今年以来&a…

软件分层测试的5大注意事项

软件分层测试作为软件测试的常见测试方法&#xff0c;有利于提高测试精度和效率&#xff0c;及早发现和解决产品缺陷和问题&#xff0c;提高了产品的质量和效率&#xff0c;降低了测试成本。如果不对软件进行分层测试&#xff0c;不利于缺陷的尽早发现和改正&#xff0c;可能会…

蓝桥杯-油漆面积

代码及其解析:(AC80%&#xff09; 思路:是把平面划成单位边长为1&#xff08;面积也是1&#xff09;的方格。每读入一个矩形&#xff0c;就把它覆盖的方格标注为已覆盖&#xff1b;对所有矩形都这样处理&#xff0c;最后统计被覆盖的方格数量即可。编码极其简单&#xff0c;但…

【鸿蒙开发】if/else条件渲染,ForEach循环渲染

if/else 使用规则 支持if、else和else if语句。if、else if后跟随的条件语句可以使用状态变量。允许在容器组件内使用&#xff0c;通过条件渲染语句构建不同的子组件。条件渲染语句在涉及到组件的父子关系时是“透明”的&#xff0c;当父组件和子组件之间存在一个或多个if语句…

STM32中C编程引入C++程序

C具备类的创建思想很实用于实际场景多相似性的框架搭建&#xff1b;同种类型或相似类型的C的优势明显因此进行相互嵌套使用 需要在C中使用C类的话&#xff0c;你可以通过C的“extern "C"”语法来实现。这允许你在C代码中使用C的链接方式&#xff0c;而在C代码中使用…

服务器开发 Socket 相关函数

Socket 函数 #include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol)domain: AF_INET 这是大多数用来产生 socket 的协议&#xff0c;使用TCP或UDP来传输&#xff0c;用IPv4的地址 AF_INET6 与上面类似&#xff0c;不过…

电商选品难?那是因为你不会用大数据选品工具…

电商选品之所以难&#xff0c;主要有以下几个方面的原因。电商市场更新换代非常快&#xff0c;新的产品不断涌现&#xff0c;旧的产品可能很快就被淘汰。电商选品紧跟市场趋势&#xff0c;不断调整和更新&#xff0c;这对电商运营市场敏感度和反应速度提出了很高的要求。 电商…

用友NC importPml SQL注入漏洞(XVE-2023-29120)

0x01 产品简介 用友NC是由用友公司开发的一套面向大型企业和集团型企业的管理软件产品系列。这一系列产品基于全球最新的互联网技术、云计算技术和移动应用技术,旨在帮助企业创新管理模式、引领商业变革。 0x02 漏洞概述 用友NC importPml接口存在SQL注入漏洞,攻击者通过利…

java操作linux

文章目录 远程连接服务器执行linux命令或shell脚本介绍Process的方法相关类UML 工具类基本工具类依赖第三方的工具类 远程连接服务器 java程序远程linux服务器有两个框架分别是&#xff1a;jsch与ganymed-ssh2框架。推荐使用jsch框架&#xff0c;因为ganymed-ssh2框架不支持麒…