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

💗个人主页💗
⭐个人专栏——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: 1foo(); // 输出 Count: 2foo(); // 输出 Count: 3return 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.hqwc.cn/news/470094.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决:RuntimeError: shape ‘[1, 3, 32, 32]‘ is invalid for input of size 4096

报错&#xff1a; 原因&#xff1a; 因为我下载的图片是4通道的&#xff1a;通过下面打印出来的结果可知是RGBA四通道的。 解决方法&#xff1a; 但是网络的输入需要RGB三通道&#xff0c;因此需要使用下列方法进行转换&#xff1a; image_path "../imgs/dog.png"…

一键式开发工具包,直接帮你安装好所有开发环境

一键式开发工具&#xff0c;你值得拥有 下载老环境说明新环境说明公共环境说明 使用 我之前一直有一个想法&#xff0c;就是有一个虚拟机环境&#xff0c; 里面装好了我所需要的所有开发环境&#xff08;Java开发、Python、前端等&#xff09; 这样的话我本地就不需要安装好多好…

算法学习——LeetCode力扣回溯篇4

算法学习——LeetCode力扣回溯篇4 332. 重新安排行程 332. 重新安排行程 - 力扣&#xff08;LeetCode&#xff09; 描述 给你一份航线列表 tickets &#xff0c;其中 tickets[i] [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。 所有这些机票…

SpringMVC-入门

1.概念 SpringMVC是一种软件架构思想&#xff0c;把软件按照模型(Model)、视图(View)、控制器(Controller)这三层来划分。Model&#xff1a;指的是工程中JavaBean&#xff0c;用来处理数据View&#xff1a;指的是工程中的html、jsp等页面&#xff0c;用来展示给用户数据Control…

vue3 之 商城项目—结算模块

路由配置 chekout/index.vue <script setup> const checkInfo {} // 订单对象 const curAddress {} // 地址对象 </script> <template><div class"xtx-pay-checkout-page"><div class"container"><div class"w…

ESP32学习(3)——连接WIFI

1.简介 Wi-Fi是基于IEEE 802.11标准的无线网络技术 让联网设备以无线电波的形式&#xff0c;加入采用TCP/IP通信协议的网络. Wi-Fi设备有两种模式&#xff1a; 1.Access Point(AP) 模式&#xff0c;此为无线接入点&#xff0c;家里的光猫就是结合WiFi和internet路由功能的AP。…

代码随想录算法训练营第32天 | 122.买卖股票的最佳时机II , 55. 跳跃游戏 , 45.跳跃游戏II

贪心算法章节理论基础&#xff1a; https://programmercarl.com/%E8%B4%AA%E5%BF%83%E7%AE%97%E6%B3%95%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html 122.买卖股票的最佳时机II 题目链接&#xff1a;https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/ 思路…

Python 读取pdf文件

Python 实现读取pdf文件简单示例。 安装命令 需要安装操作pdf的三方类库&#xff0c;命令如下&#xff1a; pip install pdfminer3K 安装过程如下&#xff1a; 引入类库 需要引入很多的类库。 示例如下&#xff1a; import sys import importlib importlib.reload(sys)fr…

Java:继承——继承概念+父子类成员、构造访问顺序+super、this关键字(代码+画图超详解!)

一、什么是继承 1、继承的概念 举例理解&#xff1a; 根据打印机的原理&#xff0c;我们可以知道不管是彩色打印机还是黑白打印机&#xff0c;实现的都是一个功能&#xff1a;打印&#xff0c;这是二者的共性。彩色打印机和黑白打印机都继承了打印机的打印功能&#xff0c;且二…

B端系统从0到1:有几步,其中需求分析要做啥?

一款B系统从无到有都经历了啥&#xff0c;而其中的需求分析又要做什么&#xff1f;贝格前端工场给老铁们做一下分析&#xff0c;文章写作不易&#xff0c;如果咱们有界面设计和前端开发需求&#xff0c;别忘了私信我呦&#xff0c;开始了。 一、B端系统从0到1都有哪些要走的步骤…

【C语言】指针专项练习 都是一些大厂的笔试真题 附有详细解析,带你深入理解指针

一.sizeof()与strlen() sizeof是一个操作符&#xff0c;而strlen是一个库函数。 数组名代表首元素地址&#xff0c;有两种情况例外&#xff0c;第一种是数组名单独放在sizeof内部&#xff0c;第二种是&数组名&#xff0c;这两种情况下数组名代表的是整个数组。sizeof(arr…

数据密集型应用系统设计

数据密集型应用系统设计 原文完整版PDF&#xff1a;https://pan.quark.cn/s/d5a34151fee9 这本书的作者是少有的从工业界干到学术界的牛人&#xff0c;知识面广得惊人&#xff0c;也善于举一反三&#xff0c;知识之间互相关联&#xff0c;比如有个地方把读路径比作programming …