C++类与对象(6)—初始化列表、explicit关键字、static成员
目录
一、初始化列表
1、定义
2、注意事项
3、尽量使用初始化列表初始化
4、初始化顺序
二、 explicit关键字
1、定义
2、特点
三、static成员
1、定义
2、特性
3、例题
一、初始化列表
下面这段代码可以正常编译:
class A {
private:int _a1;//成员声明int _a2;
};
int main()
{A a;//对象整体定义return 0;
}

如果加上一个const类型的成员变量_x,编译就无法通过。
class A {
private:int _a1;int _a2;const int _x;
};
int main()
{A a;return 0;
}

这是因为const变量必须在定义的位置初始化,否则编译不通过。
class A {
private:int _a1;//声明int _a2;const int _x;
};
在private作用域中,const变量和两个int变量都是成员变量的声明,如果我们声明const变量,一定要对它进行定义,那我们在哪定义呢?
C++11之后可以在声明位置为变量赋初值。
const int _x = 0;
那在C++11之前,也有解决方法,给每个成员变量找一个位置对其进行定义,这样就解决了变量初始化的问题,这个位置使用初始化列表进行初始化赋值。
1、定义
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
class A {
public:A():_x(1){}
private:int _a1;int _a2;const int _x;
};
int main()
{A a;return 0;
}
只要对象调用构造函数,初始化列表是它所有成员变量定义的位置。
不管是否显示在初始化列表写,那么编译器每个变量都会初始化列表定义初始化。
class A {
public:A():_x(1),_a1(6){}
private:int _a1 = 1;int _a2 = 2;const int _x;
};
int main()
{A a;return 0;
}
在初始化列表中初始化的变量,不使用缺省值;没有使用初始化列表的变量,使用缺省值。 
2、注意事项
- 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
- 类中包含以下成员,必须放在初始化列表位置进行初始化:
- 引用成员变量
- const成员变量
- 自定义类型成员(且该类没有默认构造函数时)
class B {
public:B():_b(0){cout << "B()" << endl;}
private:int _b;
};class A {
private:B _bb;
};
int main()
{A aa;return 0;
}
这里的aa的成员变量自定义类型_bb是可以调用它的默认构造函数的初始化列表进行初始化。

默认构造可以是无参或全缺省的。
class B {
public:B(int n) :_b(0)//会报错B(int n=9) :_b(0)//全缺省B( ) :_b(0)//无参
private:int _b;
};
3、尽量使用初始化列表初始化
下面看一个用两个栈实现的队列。
typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 10){cout << "Stack(size_t capacity = 10)" << endl;_array = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == _array){perror("malloc申请空间失败");exit(-1);}_size = 0;_capacity = capacity;}void Push(const DataType& data){_array[_size] = data;_size++;}Stack(const Stack& st){cout << "Stack(const Stack& st)" << endl;_array = (DataType*)malloc(sizeof(DataType)*st._capacity);if (nullptr == _array){perror("malloc申请空间失败");exit(-1);}memcpy(_array, st._array, sizeof(DataType)*st._size);_size = st._size;_capacity = st._capacity;}~Stack(){cout << "~Stack()" << endl;if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}}private:DataType *_array;size_t _size;size_t _capacity;
};class MyQueue
{
public:MyQueue(int pushN, int popN):_pushST(pushN), _popST(popN){}private:Stack _pushST;Stack _popST;int _size = 0;
};int main()
{ MyQueue q(2, 3);return 0;
}
在调试中可以看到,这里的2和3分别作为参数传递给MyQueue的构造函数,通过初始化列表对这两个成员变量进行初始化。

如果我们使用这种无参的构造函数对MyQueue对象初始化呢?
class MyQueue
{
public:MyQueue(){}private:Stack _pushST;Stack _popST;int _size = 0;
};
可以看到,如果我们不写初始化列表,MyQueue类也可以调用Stack的默认构造函数对两个Stack类的对象进行初始化,不写MyQueue的构造函数也会使用同样方式初始化,本质上一样。

4、初始化顺序
成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。
class A{
public:A(int a):_a1(a), _a2(_a1){}void Print() {cout << _a1 << " " << _a2 << endl;}
private:int _a2;int _a1;
};int main() {A aa(1);aa.Print();
}
_a2比_a1先声明,所以_a2比_a1先在初始化列表中初始化,_a2初始化时_a1还没初始化,所以_a2是随机值。

二、 explicit关键字
class A {
public:A(int a):_a1(a){}
private:int _a2;int _a1;
};
int main()
{A aa1(1); //构造函数A aa2 = 1; //隐式类型转换int i = 1; double d = i;//隐式类型转换return 0;
}
默认情况下,这里的隐式类型转换都会借助额外创建的临时变量实现,通过构造创建临时变量,然后拷贝构造给变量赋值的过程被优化为直接构造,下一篇文章详细讲解优化过程。

在这两种情况下,临时变量的创建是为了完成类型转换的过程。这些临时变量在转换完成后会被销毁,对于程序的其他部分是不可见的。这种临时变量的创建和销毁是由编译器自动处理的,无需手动干预。
-
A aa2 = 1;这里发生了从int到A的隐式类型转换。编译器会自动调用A类的构造函数来创建一个临时的A对象,然后将整数值1传递给构造函数作为参数。这个临时的A对象会被复制到aa2中,完成隐式类型转换。 -
double d = i;这里发生了从int到double的隐式类型转换。编译器会创建一个临时的double变量,并将整数变量i的值复制到这个临时变量中。然后,这个临时的double变量的值会被赋给变量d,完成隐式类型转换。
拷贝构造也属于构造,也可以使用初始化列表,但下面的成员变量会调用拷贝构造吗?
class A
{
public:A(int a):_a1(a){cout << "A(int a)" << endl;}A(const A& aa):_a1(aa._a1){cout << "A(const A& aa)" << endl;}private:int _a2;int _a1;
};int main()
{A aa1(1); //构造函数A aa2 = 1; //隐式类型转换return 0;
}
输出结果发现没有调用引用类型的拷贝构造。

这是因为C++中编译器会对自定义类型的进行优化, 将构造+拷贝+优化的过程优化成一个构造。
那下面的代码中,为什么第一个会报错,第二个没问题呢?
A& ref = 10;
const A& ref = 10;
- 这是因为在C++中,当你声明一个引用(比如
A& ref)并试图将其初始化为一个右值(比如一个临时对象或一个字面量),编译器通常会报错。这是因为非const引用不能绑定到右值上,防止对临时对象的非常量引用,因为这可能导致对临时对象的意外修改,从而导致不确定的行为。但是,当你声明一个常量引用(比如const A& ref),编译器允许这种绑定,因为常量引用可以绑定到右值上。 - 在上述代码中,
const A& ref = 10;这行代码中的10是一个整数字面量,是一个右值。你尝试将这个右值绑定到引用ref上。由于ref被声明为const A&,它是一个常量引用,所以编译器允许这种绑定,并调用A类的构造函数A(int a)来创建一个临时的A对象,然后将ref绑定到这个临时对象上。
1、定义
对于单参构造函数:没有使用explicit修饰,具有类型转换作用explicit修饰构造函数,禁止类型转换---explicit去掉之后,代码可以通过编译。
对于刚刚的代码,如果在构造函数前加explicit程序会怎么样呢?
class A{
public:explicit A(int a):_a1(a){cout << "A(int a)" << endl;}A(const A& aa):_a1(aa._a1){cout << "A(const A& aa)" << endl;}private:int _a2;int _a1;
};
int main()
{A aa1(1); //构造函数A aa2 = 1; //隐式类型转换const A& ref = 10;return 0;
}
这两段代码会报错,程序禁止类型转换。

2、特点
class A
{
public://explicit A(int a)A(int a):_a1(a){cout << "A(int a)" << endl;}//explicit A(int a1, int a2)A(int a1, int a2):_a1(a1), _a2(a2){}private:int _a2;int _a1;
};
int main()
{// 单参数构造函数 C++98A aa1(1); //构造函数A aa2 = 1; //隐式类型转换// 多参数构造函数 C++11A aa2(1, 1);//A aa3= 2,2;//C98不支持A aa3 = { 2, 2 };//C++11支持return 0;
}
-
A aa1(1);这是直接调用单参数构造函数创建对象的例子。 -
A aa2 = 1;这是一个隐式类型转换的例子。这里,整数1被隐式地转换为类A的一个对象。这是因为类A定义了一个接受int类型参数的构造函数,因此编译器会自动调用该构造函数来创建一个临时的A对象,并将其赋值给aa2。 -
A aa2(1, 1);这是直接调用双参数构造函数创建对象的例子。 -
A aa3 = { 2, 2 };这是C++11引入的列表初始化的例子。这种方式可以用来初始化对象,而不需要显式地调用构造函数。
explicit关键字用于阻止编译器进行不希望发生的隐式类型转换。如果你将构造函数前面的注释去掉,使得构造函数前面有explicit关键字,那么像A aa2 = 1;这样的隐式类型转换就会被禁止,编译器会报错。
例如,如果你将单参数构造函数改为explicit A(int a),那么A aa2 = 1;这行代码就会导致编译错误,因为编译器被禁止进行从int到A的隐式类型转换。你必须显式地调用构造函数,像A aa2(1);这样。
总的来说,explicit关键字可以帮助你控制类型转换,防止因为不希望的隐式类型转换而导致的错误。
三、static成员
实现一个类,计算程序中创建了多少类对象
int count = 0;
class A
{
public:A(int a = 0){++count;}A(const A& aa){++count;}
};
void func(A a)
{}
int main()
{A aa1;A aa2(aa1);func(aa1);A aa3 = 1;cout << count << endl;return 0;
}
造成了命名冲突的问题,因为C++的xutility文件里有个函数count与我们定义的全局变量count冲突了。

我们可以不展开std,只调用需要用的流输入输出即可。
#include <iostream>
//using namespace std;
using std::cout;
using std::endl;
成功输出:

C++为了解决上述问题,同时可以将std展开,将count作为类的static修饰的成员即可实现。
1、定义
- 声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;
- 用static修饰的成员函数,称之为静态成员函数。
- 静态成员变量一定要在类外进行初始化。
- 静态成员不属于某个对象,所于所有对象,属于整个类。
- 静态成员变量的初始化通常在类外部进行。
class A
{
public:A(int a = 0){++count;}A(const A& aa){++count;}int Getcount(){return count;}
private:static int count; // 此处为声明int _a = 0;
};int A::count = 0; // 定义初始化void func(A a)
{} 当我们想输出时:
int main()
{A aa1;A aa2(aa1);func(aa1);A aa3 = 1;cout << A::Getcount() << endl;return 0;
}
如果想要输出,可以使用静态成员函数。
//静态成员函数 没有this指针static int Getcount(){// _a++; // 不能直接访问非静态成员return count;} 成功输出:

下面语句创建出了多少个类对象?
A aa4[10]; 输出结果:
2、特性
- 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
- 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
- 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
- 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
- 静态成员也是类的成员,受public、protected、private 访问限定符的限制
3、例题
求1+2+3+...+n_牛客题霸_牛客网 (nowcoder.com)

- 下面这段代码实现了一个类
Sum和一个类Solution,其中Sum类用于计算从1到n的累加和,而Solution类则使用Sum类来计算给定整数n的累加和。- 这种设计利用了类的构造函数和静态成员变量的特性,实现了累加和的计算和获取。
class Sum{
public:Sum(){_sum+=_i;_i++;}static int Getsum(){return _sum;}
private:static int _sum;static int _i;
};
int Sum::_sum = 0;
int Sum::_i = 1;
class Solution {
public:int Sum_Solution(int n) {Sum a[n];return Sum::Getsum();}
};
首先,让我们逐步解释 Sum 类的实现:
Sum类有两个静态成员变量_sum和_i,分别用于保存累加和和当前的计数器值。- 构造函数
Sum()是一个无参构造函数,每次被调用时,它会将当前计数器值_i加到累加和_sum中,并将计数器_i自增1。 - 静态成员函数
Getsum()用于获取累加和_sum的值。
接下来,我们来看 Solution 类的实现:
Solution类中的成员函数Sum_Solution(int n)接受一个整数n作为参数,并返回从1到n的累加和。- 在
Sum_Solution函数中,我们创建了一个名为a的Sum类型的数组,数组的大小为n。 - 由于
Sum类的构造函数会在创建对象时自动调用,因此创建数组a的过程中,会依次调用Sum类的构造函数,从而实现了从1到n的累加和的计算。 - 最后,我们通过调用
Sum::Getsum()函数来获取累加和的值,并将其作为函数的返回值。
相关文章:
C++类与对象(6)—初始化列表、explicit关键字、static成员
目录 一、初始化列表 1、定义 2、注意事项 3、尽量使用初始化列表初始化 4、初始化顺序 二、 explicit关键字 1、定义 2、特点 三、static成员 1、定义 2、特性 3、例题 一、初始化列表 下面这段代码可以正常编译: class A { private:int _a1;//成员…...
vue3+tsx的使用
<template><div><xiaoman on-click"getItem" name"似懂非懂"></xiaoman></div> </template><script setup langts>import xiaoman from "./App"const getItem(item:any)>{console.log(item,it…...
JMeter 设置请求头信息的详细步骤
在使用 JMeter 的过程中,我们会遇到需要设置请求头信息的场景。比如: POST 传过去的 Body 数据是 json 格式的。需要填添加头信息:Content-Type:application/json。 在 header 中用 token 来传用户的认证信息。 下面,…...
从零构建属于自己的GPT系列1:预处理模块
1 训练数据 在本任务的训练数据中,我选择了金庸的15本小说,全部都是txt文件 数据打开后的样子 2 数据预处理 数据预处理需要做的事情就是使用huggingface的transformers包的tokenizer模块,将文本转化为token 最后生成的文件就是train_n…...
002、ArkTS
之——开发语言 目录 之——开发语言 杂谈 正文 1.TypeScript基础 1.1 基础类型 1.2 条件语句 1.3 函数 1.4 类 1.5 模块 1.6 迭代器 2.ArkTS 2.1 JAVA SCRIPT 2.2 TS 2.3 ArkTS 编辑 3.示例 3.1 概述性示例 3.2 自定义组件 3.3 渲染控制语法 3.4 状态管…...
如何通过nginx进行服务的负载均衡
简单介绍 随着互联网的发展,业务流量越来越大并且业务逻辑也越来越复杂,单台服务器的性能及单点故障问题就凸显出来了,因此需要多台服务器组成应用集群,进行性能的水平扩展以及避免单点故障的出现。应用集群是将同一应用部署到多台…...
FPGA程序前仿真和后仿真问题处理
参考链接:FPGA程序前仿真和后仿真问题处理 - 知乎...
C语言WFC绘制矩形
代码实现: void CCGDrawingView::Rectangle(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, COLORREF color,CDC* pDC) {CPen redPen(PS_SOLID, 1, color);CBrush redBursh(color);CPen* pOldPen pDC->SelectObject(&redPen);CBrush* p…...
SpringCloud Alibaba集成 Gateway(自定义负载均衡器)、Nacos(配置中心、注册中心)、loadbalancer
文章目录 POM依赖环境准备配置配置文件配置类 案例展示 POM依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.10</version><relativePath/></p…...
HarmonyOS应用开发者基础认证【题库答案】
HarmonyOS应用开发者高级认证【题库答案】 一、判断 首选项preferences是以Key-Value形式存储数据,其中Key是可以重复。(错)使用http模块发起网络请求时,必须要使用on(‘headersReceive’)订阅请求头,请…...
[pyqt5]pyqt5设置窗口背景图片后上面所有图片都会变成和背景图片一样
pyqt5的控件所有都是集成widget,窗体设置背景图片后控件背景也会跟着改变,此时有2个办法。第一个办法显然我们可以换成其他方式设置窗口背景图片,而不是使用styleSheet样式表,网上有很多其他方法。还有个办法就是仍然用styleSheet…...
【Docker】从零开始:7.Docker命令:容器命令及参数详解
【Docker】从零开始:7.帮助启动类命令 一、帮助启动类命令启动Docker停止Docker重启Docker查看Docker状态开机启动查看docker概要信息查看docker总体帮助文档查看docker命令帮助文档 二、镜像命令列出本地主机上的镜像运行示例返回说明操作参数 搜索仓库里的某个镜像…...
Mysql 锁机制分析
整体业务代码精简逻辑如下: Transaction public void service(Integer id) {delete(id);insert(id); }数据库实例监控: 当时通过分析上游问题流量限流解决后,后续找时间又重新分析了下问题发生的根本原因,现将其总结如下…...
跟着chatgpt学习|1.spark入门
首先先让chatgpt帮我规划学习路径,使用Markdown格式返回,并转成思维导图的形式 目录 目录 1. 了解spark 1.1 Spark的概念 1.2 Spark的架构 1.3 Spark的基本功能 2.spark中的数据抽象和操作方式 2.1.RDD(弹性分布式数据集) 2…...
使用conan包 - 安装依赖项
使用conan包 - 安装依赖项 主目录 conan Using packages1 Requires2 Optional user/channel3 Overriding requirements4 Generators5 Options 本文是基于对conan官方文档Installing dependencies的翻译而来, 更详细的信息可以去查阅conan官方文档。 This section s…...
【数据库设计和SQL基础语法】--数据库设计基础--数据规范化和反规范化
一、 数据规范化 1.1 数据规范化的概念 定义 数据规范化是数据库设计中的一种方法,通过组织表结构,减少数据冗余,提高数据一致性和降低更新异常的过程。这一过程确保数据库中的数据结构遵循一定的标准和规范,使得数据存储更加高…...
复亚智能交通无人机:智慧交通解决方案大公开
城市的现代化发展离不开高效的交通管理规划。传统的交通管理系统庞大繁琐,交警在执行任务时存在安全隐患。在这一背景下,复亚智能交通无人机应运而生,成为智慧交通管理中的重要组成部分。交通无人机凭借其高灵活性、低成本、高安全性等特点&a…...
MYSQL 及 SQL 注入
文章目录 前言什么是sql注入防止SQL注入Like语句中的注入后言 前言 hello world欢迎来到前端的新世界 😜当前文章系列专栏:Mysql 🐱👓博主在前端领域还有很多知识和技术需要掌握,正在不断努力填补技术短板。(如果出现…...
古埃及金字塔的修建
从理论上说,古埃及人完全有能力设计并建造出充满各种奇妙细节的胡夫金字塔,但后世还是不断涌现出质疑之声,原因倒也简单,那就是胡夫金字塔实在太大了。据推算,整座金字塔使用大约230万块巨石,总质量可达约5…...
Android 13.0 系统settings系统属性控制一级菜单显示隐藏
1.概述 在13.0的系统rom定制化开发中,系统settings的一级菜单有些在客户需求中需要去掉不显示,所以就需要通过系统属性来控制显示隐藏, 从而达到控制一级菜单的显示的目的,而系统settings是通过静态加载的方式负责显示隐藏,接下来就来实现隐藏显示一级菜单的 功能实现 2.…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
