【C++】const、static关键字和构造函数初始化

news2025/1/18 17:08:39

💗个人主页💗
⭐个人专栏——C++学习⭐
💫点击关注🤩一起学习C语言💯💫

目录

1. const修饰成员函数

1.1 语法格式

1.2 权限放大缩小

1.3 思考

1.4 解答

2. 再谈构造函数

2.1 构造函数体赋值

2.2 初始化列表

2.3 explicit关键字

3. static成员

3.1 静态变量

3.2 静态函数

3.3 静态成员变量


1. const修饰成员函数

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

1.1 语法格式

在成员函数的声明中使用const关键字的语法格式为:

返回类型 函数名() const;

在成员函数的定义中使用const关键字的语法格式为:

返回类型 类名::函数名() const {
    // 函数体
}

1.2 权限放大缩小

const 修饰的权限放大(扩大)或缩小(缩小)取决于成员函数和成员变量的修饰符。

  1. 成员函数:

    • 如果一个非const成员函数被const修饰符修饰,它的权限会缩小,即它不能修改对象的非mutable成员变量,也不能调用非const成员函数。
    • 如果一个const成员函数被const修饰符修饰,它的权限保持不变,即它不能修改对象的任何成员变量,也不能调用非const成员函数。
  2. 成员变量:

    • 如果一个非const成员变量被const修饰符修饰,它的权限会缩小,即它只能被const成员函数访问,而非const成员函数不能修改它。
    • 如果一个const成员变量被const修饰符修饰,它的权限保持不变,即它不能修改对象的任何成员变量,也不能调用非const成员函数。
class Example {
private:
    int data;
    const int constData;
    mutable int mutableData;

public:
    void nonConstFunc() {
        data = 10; // 可以修改非const成员变量
        constData = 20; // 错误!不能修改const成员变量
        mutableData = 30; // 可以修改mutable成员变量
        constFunc(); // 可以调用const成员函数
        nonConstMemberFunc(); // 可以调用非const成员函数
    }

    void constFunc() const {
        data = 10; // 错误!不能修改非mutable成员变量
        constData = 20; // 错误!不能修改const成员变量
        mutableData = 30; // 可以修改mutable成员变量
        nonConstMemberFunc(); // 错误!不能调用非const成员函数
    }

    void nonConstMemberFunc() {
        // 省略函数体
    }
};

1.3 思考

1. const对象可以调用非const成员函数吗?

2. 非const对象可以调用const成员函数吗?

3. const成员函数内可以调用其它的非const成员函数吗?

4. 非const成员函数内可以调用其它的const成员函数吗?

1.4 解答

1. const对象不能调用非const成员函数。

const对象被视为只读对象,它的成员函数不能修改对象的状态。

因此,只有被声明为常成员函数的成员函数才能在const对象上调用。

#include <iostream>
using namespace std;


class Example 
{
public:
    void nonConstFunc() 
    {
        cout << "Non-const member function." << endl;
    }

    void constFunc() const 
    {
        cout << "Const member function." << endl;
    }
};

int main() 
{
    const Example obj;
    obj.constFunc(); // 可以调用const成员函数
    obj.nonConstFunc(); // 错误!不能调用非const成员函数

    return 0;
}

注释掉 “ obj.nonConstFunc(); ” 之后:

2. 非const对象可以调用const成员函数。

const成员函数是一种特殊的成员函数,它承诺不修改对象的状态。

因此,无论是非const对象还是const对象,都可以调用const成员函数。

#include <iostream>
using namespace std;

class Example {
public:
    void nonConstFunc() 
    {
        cout << "Non-const member function." << endl;
    }

    void constFunc() const 
    {
        cout << "Const member function." << endl;
    }
};

int main() 
{
    Example obj;
    obj.constFunc(); // 可以调用const成员函数
    obj.nonConstFunc(); // 可以调用非const成员函数

    return 0;
}

 3. const成员函数内不可以调用其他非const成员函数。

const成员函数调用非const成员函数,调用该const成员函数的对象已经被设置为const类型,只可以访问不能修改,在用该const成员函数访问其他非const成员函数可能会修改,因此const成员函数不能调用非const成员函数。

#include <iostream>
using namespace std;

class Example 
{
public:
    void nonConstFunc() 
    {
        cout << "Non-const member function." << endl;
    }

    void constFunc() const 
    {
        cout << "Const member function." << endl;
        nonConstFunc(); // 可以调用非const成员函数
    }
};

int main() {
    Example obj;
    obj.constFunc();

    return 0;
}

4.  非const成员函数内可以调用其他的const成员函数。

非const成员函数可以修改对象的状态,因此它在内部调用const成员函数时可以保证对象的状态不被修改。

#include <iostream>
using namespace std;

class Example {
private:
    int data;

public:
    Example(int val) : data(val) 
    {}

    void nonConstFunc() {
        cout << "Non-const member function." << endl;
        constFunc(); // 可以调用const成员函数
    }

    void constFunc() const {
        cout << "Const member function." << endl;
        cout << "Data: " << data << endl;
    }
};

int main() 
{
    Example obj(10);
    obj.nonConstFunc();

    return 0;
}

2. 再谈构造函数

2.1 构造函数体赋值

在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。

class Date
{
public:
Date(int year, int month, int day)
 {
     _year = year;
     _month = month;
     _day = day;
 }
private:
int _year;
int _month;
int _day;
};

虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量 的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始 化一次,而构造函数体内可以多次赋值。

2.2 初始化列表

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟 一个放在括号中的初始值或表达式。

class Date
{
public:
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};

【注意】

1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)

2. 类中包含以下成员,必须放在初始化列表位置进行初始化:

  • 引用成员变量
  • const成员变量
  • 自定义类型成员(且该类没有默认构造函数时)

3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量, 一定会先使用初始化列表初始化。

4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后 次序无关

class A
{
public:
	//A(int a = 0, int b = 1)
	A(int a, int b)
		:_a(a)
	{
		cout << "A(int a = 0)" << endl;
	}

private:
	int _a;
};

class Date
{
public:
	// 初始化列表是每个成员变量定义初始化的位置
	// 能用初始化列表就建议用初始化列表
	Date(int year, int month, int day, int& x)
		:_year(year)
		,_month(month)
		,_day(day)
		,_n(1)
		,_ref(x)
		,_aa(1, 2)
		,_p((int*)malloc(sizeof(4) * 10))
	{
		if (_p == nullptr)
		{
			perror("malloc fail");
		}

		for (size_t i = 0; i < 10; i++)
		{
			_p[i] = 0;
		}
	}

private:
	// 声明
	int _year;
	int _month;
	int _day;

	// 必须走初始化
	const int _n;
	int& _ref;
	A _aa;

	int* _p;
};

int main()
{
	// 对象实例化
	int x = 10;
	Date d1(2024, 1, 31, x);

	//A aa(2, 3);
 
	return 0;
}

2.3 explicit关键字

explicit是一个关键字,用于声明构造函数为显示构造函数。

当一个构造函数被声明为explicit时,它不能被用于隐式转换,只能被用于显式地创建对象。这个关键字通常用于防止意外的隐式类型转换。

class MyInt 
{
public:
    explicit MyInt(int i) 
        : value(i) 
    {}
    int getValue() const 
    { 
        return value; 
    }

private:
    int value;
};

void foo(MyInt mi) 
{
    int i = mi.getValue();
    // ...
}

int main() {
    MyInt mi1 = 42;      // 错误,不能进行隐式转换
    MyInt mi2(42);       // 正确,可以显式创建对象
    foo(42);             // 错误,不能进行隐式转换
    foo(MyInt(42));      // 正确,可以显式创建对象
    return 0;
}

3. static成员

static是用于修饰变量、函数和类成员的关键字。它可以有不同的作用,具体取决于它应用的位置。

3.1 静态变量

在函数内部使用static修饰的变量称为静态变量,它的生命周期是整个程序运行期间,而不是仅在函数调用时存在。

静态变量在内存中只有一个副本,且在第一次初始化时赋值,并且在程序结束时销毁。静态变量默认为0,除非显式初始化。

void foo() 
{
	static int count = 0; // 静态变量
	count++;
	cout << "Count: " << count << endl;
}

int main() {
	foo(); // 输出 Count: 1
	foo(); // 输出 Count: 2
	foo(); // 输出 Count: 3
	return 0;
}

3.2 静态函数

在类内部使用static修饰的函数称为静态函数,它与类的实例无关,可以直接通过类名调用,而不需要创建对象。

静态函数只能访问类的静态成员变量和其他静态函数,不能访问非静态成员变量和非静态函数。

class MyClass 
{
public:
    static void myStaticFunc() 
    {
        cout << "Static function!" << endl;
    }
};

int main() 
{
    MyClass::myStaticFunc(); // 直接调用静态函数
    return 0;
}

静态函数的特点:

  1. 静态函数属于类本身,而不是类的实例。
  2. 静态函数可以直接通过类名调用,不需要通过对象来调用。
  3. 静态函数无法访问非静态成员变量和非静态成员函数,因为它们是与对象相关的。
  4. 静态函数可以访问静态成员变量和静态成员函数。
  5. 静态函数在类的范围内具有全局可见性,可以在其他源文件中使用类名来调用静态函数。

 

3.3 静态成员变量

在类内部使用static修饰的成员变量称为静态成员变量,它是类的所有实例共享的,不属于任何特定对象。

静态成员变量在类内部声明,并在类外部进行定义和初始化。定义时需要指定数据类型,并且通常需要提供一个初始值。

声明静态成员变量:

class MyClass {
public:
    static int myStaticVar; // 静态成员变量的声明
};

定义和初始化静态成员变量(在类外部):

int MyClass::myStaticVar = 0; // 静态成员变量的定义和初始化

静态成员变量可以像普通成员变量一样访问,可以通过类名和作用域运算符::,或者通过对象名和点操作符.来访问。

MyClass::myStaticVar = 5; // 通过类名访问静态成员变量
MyClass obj;
obj.myStaticVar = 10; // 通过对象名访问静态成员变量

静态成员变量的特点:

  1. 所有的类对象共享同一个静态成员变量。
  2. 静态成员变量在程序开始时初始化,并在程序结束时销毁。
  3. 静态成员变量可以被所有对象访问,但只有一个副本。
  4. 静态成员变量可以被类的所有成员函数访问,包括静态成员函数和非静态成员函数。

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

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

相关文章

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Navigation组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Navigation组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Navigation组件 鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#…

Mybatis开发辅助神器p6spy

Mybatis什么都好&#xff0c;就是不能打印完整的SQL语句&#xff0c;虽然可以根据数据来判断一二&#xff0c;但始终不能直观的看到实际语句。这对我们想用完整语句去数据库里执行&#xff0c;带来了不便。 怎么说呢不管用其他什么方式来实现完整语句&#xff0c;都始终不是Myb…

相机图像质量研究(18)常见问题总结:CMOS期间对成像的影响--CFA

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

B2科目二考试项目笔记

B2科目二考试项目笔记 1 桩考1.1 右起点倒库1.2 移库&#xff08;左→右&#xff09;1.3 驶向左起点1.4 左起点倒库1.5 驶向右起点 2 侧方停车考试阶段&#xff08;从路边开始&#xff09;&#xff1a; 3 直角转弯4 坡道定点停车和起步5 单边桥6 通过限速限宽门7 曲线行驶8 连续…

每日五道java面试题之java基础篇(六)

目录&#xff1a; 第一题&#xff1a;Java 创建对象有哪⼏种⽅式&#xff1f;第二题 .Integer a 127&#xff0c;Integer b 127&#xff1b;Integer c 128&#xff0c;Integer d 128&#xff1b;相等吗?第三题.Object 类的常⻅⽅法?第四题 List和Set的区别第五题 ArrayList和…

代码解读:Stable Video Diffusion 中对运动程度的控制

Diffusion Models视频生成-博客汇总 前言&#xff1a;在SVD中&#xff0c;对运动的控制可以分成对镜头运动的控制和对内容运动的控制&#xff0c;这篇博客详细通过代码讲解如何对内容运动的控制。 目录 方法一&#xff1a;motion_bucket_id 简述 代码解读 方法二&#xff1…

【Linux】yum软件包管理器

目录 Linux 软件包管理器 yum 什么是软件包 Linux安装软件 查看软件包 关于rzsz Linux卸载软件 查看yum源 扩展yum源下载 Linux开发工具 vim编辑器 上述vim三种模式之间的切换总结&#xff1a; 命令模式下&#xff0c;一些命令&#xff1a; vim配置 Linux 软件包管理…

BUGKU-WEB 社工-初步收集

题目描述 题目截图如下&#xff1a; 描述:其实是杂项&#xff0c;勉强算社工吧。来自当年实战 进入场景看看&#xff1a; 解题思路 做题先看源码关注可下载的资源(zip压缩包)抓包寻找可能存在的加密信息&#xff08;base64&#xff09;不管三七二十一先扫描目录再说 ps&…

【小记】MacOS Install golang

问题 - command not found: go ➜ brew install golang ➜ go version go version go1.21.7 darwin/arm64写在最后&#xff1a;若本文章对您有帮助&#xff0c;请点个赞啦 ٩(๑•̀ω•́๑)۶

.target勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复

导言&#xff1a; 网络安全威胁如勒索病毒已经成为企业和个人数据安全的重大挑战之一。.target勒索病毒作为其中的一种&#xff0c;以其高度复杂的加密算法和迅速变化的攻击手法备受关注。本文将深入介绍.target勒索病毒的特点&#xff0c;探讨如何有效地恢复被加密的数据文件…

计网day3

四 链路层 4.1 基本概念 4.2 封装成帧 4.3 差错控制 4.4 流量控制与可靠传输机制 停止-等待协议&#xff1a; 后退N帧协议&#xff08;GBN&#xff09; 选择重传协议&#xff08;SR&#xff09;&#xff1a; 4.5 信道划分介质访问控制 ALOHA协议&#…

大厂的供应链采购系统设计

关注我&#xff0c;紧跟本系列专栏文章&#xff0c;咱们下篇再续&#xff01; 作者简介&#xff1a;魔都技术专家兼架构&#xff0c;多家大厂后端一线研发经验&#xff0c;各大技术社区头部专家博主&#xff0c;编程严选网创始人。具有丰富的引领团队经验&#xff0c;深厚业务架…

嵌入式CAN通信协议原理(下)

本篇文章结合实际CAN控制器继续介绍协议相关的内容&#xff0c;还有示例讲解。 好了&#xff0c;继续吧&#xff01; 二. STM32 CAN 控制器介绍 STM32 的芯片中具有 bxCAN 控制器 (Basic Extended CAN)&#xff0c;它支持 CAN 协议 2.0A 和 2.0B 标准。 该 CAN 控制器支持最…

【JAVA】计算机软件工程人工智能研究生复试资料整理

1、JAVA 2、计算机网络 3、计算机体系结构 4、数据库 5、计算机租场原理 6、软件工程 7、大数据 8、英文 自我介绍 1. Java 1. == 和 equals的区别 比较基本数据类型是比较的值,引用数据类型是比较两个是不是同一个对象,也就是引用是否指向同 一个对象,地址是否相同,equ…

【Java】零基础蓝桥杯算法学习——线性动态规划(一维dp)

线性dp——一维动态规划 1、考虑最后一步可以由哪些状态得到&#xff0c;推出转移方程 2、考虑当前状态与哪些参数有关系&#xff0c;定义几维数组来表示当前状态 3、计算时间复杂度&#xff0c;判断是否需要进行优化。 一维动态规划例题&#xff1a;最大上升子序列问题 Java参…

【C++第二阶段-重载-关系运算符函数调用】

你好你好&#xff01; 以下内容仅为当前认识&#xff0c;可能有不足之处&#xff0c;欢迎讨论&#xff01; 文章目录 关系运算符-重载-判断相等函数调用运算符重载 关系运算符-重载-判断相等 场景&#xff1a;两个对象&#xff0c;若有年龄和性别的不同&#xff0c;是否可以直…

算法学习——LeetCode力扣贪心篇1

算法学习——LeetCode力扣贪心篇1 455. 分发饼干 455. 分发饼干 - 力扣&#xff08;LeetCode&#xff09; 描述 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。 对每个孩子 i&#xff0c;都有一个胃口值 g[…

android 控制台输出 缺失

问题 android 控制台输出内容缺失 详细问题 笔者进行android开发&#xff0c;期望控制台打印Log日志或是输出内容 Log.i("tag","content");或 System.out.println("content")但是实际上&#xff0c;上述内容并没有按照笔者期望打印 解决方…

【sgSearch】自定义组件:常用搜索栏筛选框组件(包括表格高度变化兼容)。

sgSearch源码 <template><div :class"$options.name" :expand"expandSearch" :showCollapseBtn"showCollapseBtn"><!-- v-clickoutside"(d) > (expandSearch false)" --><ul class"search-list"&…

代码随想录算法训练营Day57|647. 回文子串、516.最长回文子序列、动态规划总结

目录 647. 回文子串 前言 思路 算法实现 516.最长回文子序列 前言 思路 算法实现 动态规划总结 动规五部曲回顾 动规各小专题问题 647. 回文子串 题目链接 文章链接 前言 本题利用动态规划求解时&#xff0c;dp数组的定义与前面的就有些不同了&#xff0c;是难点之…