【C++学习】类和对象 | 再谈构造函数 | 构造函数中的隐式类型转换 | static静态成员

news2024/11/17 11:57:12

目录

1. 再谈构造函数

2. 构造函数中的隐式类型转换

3. static静态成员

写在最后:


1. 再谈构造函数

我们之前使用构造函数初始化:

#include <iostream>
using namespace std;

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

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;

	return 0;
}

使用的是在构造函数的函数体内赋值,

实际上,构造函数还有一种初识化方法,就是列表初始化,

来看代码:

#include <iostream>
using namespace std;

class Date {
public:
	Date(int year = 2023, int month = 7, int day = 1) 
		: _year(year)
		, _month(month)
		, _day(day)
	{}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;

	return 0;
}

实际上这两种初始化方式效果是一样的,

初识化列表就是以冒号开始,通过逗号分隔。

这里有几点要注意的:

1. 每个成员变量最多只能在初始化列表出现一次,

2. 有些成员必须在初始化列表初识化:

引用成员变量,const成员变量,自定义类型成员。

那这是为什么呢?

实际上,引用成员变量和const成员变量需要在定义时初始化,

而在函数体内赋值,编译器不认为是在定义时初始化,

而使用初始化列表则是在定义时初始化,

还记得我们之前学的,在函数成员变量声明时添加的缺省值吗?

那个就是在初始化列表的时候自动调用。

而对于自定义,使用列表初始化:

#include <iostream>
using namespace std;

class A {
public:
	A(int a)
		: _a(a)
	{}

private:
	int _a;
};

class B {
public:
	B(int a = 10)
		: _a(a)
	{}

private:
	A _a;
};

int main()
{
	B b;
	B b(20);

	return 0;
}

这样就能做到在创建B的对象的时候,在外面控制A类对象的初始化,

当然你也可以选择不控制,可以感受到,这样的代码能更加灵活。  

初始化列表还有一个需要注意的点:

来看这道题:

#include <iostream>
using namespace std;

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();
} 

// A.输出1 1
// B.程序崩溃
// C.编译不通过
// D.输出1 随机值

这段代码,我们肯定不能选C,因为没有语法错误,

而我们也不能选B,因为只有像空指针,越界访问等等才可能崩溃,

而我们也不应该选A,因为一般来说,选A岂不是太简单了,

那为什么答案选D,输出1和随机值呢?

因为初始化列表的初始化顺序不是按照你初始化的顺序初始化的,

他是按照声明成员变量的顺序初始化的,

所以这段代码会先初始化_a2,而_a1还是随机值,所以_a2的值被初始化成随机值,

之后再初始化_a1为1,所以输出 1 和 随机值。 

2. 构造函数中的隐式类型转换

在C++中支持这样一种操作:

#include <iostream>
using namespace std;

class A {
public:
	A(int a = 10)
		: _a(a)
	{}

private:
	int _a;
};

int main()
{
	A a1(20);
	A a2 = 20;

	return 0;
}

明明A是一个自定义类型,为啥他能 = 一个int类型的数呢?

实际上在C语言的时候我们学习过隐式类型转换:

double a = 10;

编译器将10这个整形拷贝生成一份double类型的临时变量,然后再赋值给a,

而上面那份代码也是类似的逻辑,

先用20作为参数构造生成一个A类型的临时变量,

然后a2拷贝构造形成参数为20的这个对象。

这个时候你可能会有疑问,这个特性有啥用呢?

来看这个例子:

#include <iostream>
#include <string>
using namespace std;

int main()
{
	string s1("你好");
	string s2 = "你好"; //隐式类型转换

	return 0;
}

使用string类的时候,我们可以用构造函数将"你好"这个字符串,

转换成string类型,也可以使用 = 通过隐式类型转换直接使用,

这个时候你可能又有疑问了,这两种方法好像也没方便多少啊,

我们继续来看这个例子:

#include <iostream>
#include <string>
using namespace std;

class list {
public:
	void push_back(const string& str) {

	}
};

int main()
{

	list st;

	string s1("你好");
	st.push_back(s1);

	st.push_back("你好"); //隐式类型转换

	return 0;
}

这个list类的push_back 接口需要传递string类型的参数,

如果我们没有隐式类型转换的话,就得先构造一个string类,在调用这个接口,

如果有隐式类型转换,我们就能直接传一个字符串,

然后编译器通过隐式类型转换帮我们转换成string类在传参,这就方便多了。

然后这里顺便介绍一个关键字:

关键字explicit:

使用了这个关键字就不能进行隐式类型转换了:

 可以看到,再进行隐式类型转换就报错了。

3. static静态成员

来看这个例子:

#include <iostream>
using namespace std;

class A {
public:
	A(int a = 10)
		: _a(a)
	{}

private:
	int _a;

	static int _num;
};

int A::_num = 0;

int main()
{
	A a1(20);
	A a2 = 20;

	return 0;
}

静态成员变量不能在类内定义,

需要在类外面定义,也就是这个:int  A::_num = 0

而静态成员和普通成员变量有什么区别呢?

成员变量时属于每一个实例化出来的类对象的,存储在对象里面;

而静态成员变量属于每一个类,类的每个对象都是共享的,存储在静态区。

除了静态成员变量,还有静态成员函数,

来看例子:

#include <iostream>
using namespace std;

class A {
public:
	A(int a = 10)
		: _a(a)
	{}

	static void Print() {
		cout << "I am static" << endl;
	}

private: 
	int _a;

	static int _num;
};

int A::_num = 0;

int main()
{
	A a1(20);
	A a2 = 20;

	A::Print();

	return 0;
}

输出:

I am static

静态成员函数没有this指针,

直接通过指定类域和访问限定符就可以访问。

那我们来实践一下:

如果我们想设计一个类,在类外面只能在栈/堆上创建对象该怎么做?

来看代码:

#include <iostream>
using namespace std;

class A {
public:
	A(int a = 10)
		: _a(a)
	{}

private: 
	int _a;
};

int main()
{
	static A a;

	return 0;
}

如果只是一个普通的类,这就在静态区上创建出一个对象了,

那该怎么限制它呢?

我们可以把构造函数放到私有:

#include <iostream>
using namespace std;

class A {
public:

private:
	A(int a = 10)
		: _a(a)
	{}

private: 
	int _a;
};

int main()
{
	A a;         //在栈上开辟
	A* a = new A; //在堆上开辟

	return 0;
}

这下好了,虽然静态区不能创建了,但是你在哪里都不能创建对象了,

因为构造函数只能在类内调用了,这该怎么办呢?

这个时候,static静态成员函数就可以出场了:

#include <iostream>
using namespace std;

class A {
public:
	static A* GetHeap() {
		return new A;
	}

	static A& GetStack() {
		A a;
		return a;
	}

private:
	A(int a = 10)
		: _a(a)
	{}

private: 
	int _a;
};

int main()
{
	//A a;          //在栈上开辟
	//A* a = new A; //在堆上开辟

	A::GetStack(); //在栈上开辟
	A::GetHeap();  //在堆上开辟

	return 0;
}

你看,我们使用静态成员函数是不是一下子就解决了。

写在最后:

以上就是本篇文章的内容了,感谢你的阅读。

如果感到有所收获的话可以给博主点一个哦。

如果文章内容有遗漏或者错误的地方欢迎私信博主或者在评论区指出~

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

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

相关文章

arcgis js 通过某一个经纬度 定位报错,并且图标变得很大【已解决】

报错 svg.js:42 Error: attribute transform: Expected number, “…0000,0.02102085,NaN,NaN)”. svg.js:49 Error: attribute x: Expected length, “NaN”. svg.js:49 Error: attribute y: Expected length, “NaN”. 图标特别大&#xff0c;也看不到地图 分析 这个方法中…

智驾“平价”,小鹏G6打特斯拉是认真的

作者|张祥威编辑|德新 “小鹏在辅助驾驶领域不是遥遥领先&#xff0c;而是领先友商 12 - 36 个月。” “希望L4的能力能够在2027年到来&#xff0c;或者更早一点。” “G6的销量肯定要过万&#xff0c;这是最起码的。” G6上市发布期间&#xff0c;小鹏的高管各种喊话。 抛开80…

(嵌入式)STM32G061C8T6、STM32G061C6T6、STM32G061C8U6 64MHz 64KB/32KB 闪存(MCU)

STM32G0 32位微控制器 (MCU) 适合用于消费、工业和家电领域的应用&#xff0c;并可随时用于物联网 (IoT) 解决方案。这些微控制器具有很高的集成度&#xff0c;基于高性能ARM Cortex-M0 32位RISC内核&#xff0c;工作频率高达64MHz。该器件包含内存保护单元 (MPU)、高速嵌入式内…

算法笔记--滑动窗口

力扣209.长度最小子数组 https://leetcode.cn/problems/minimum-size-subarray-sum/ 在这道题中要注意的不仅仅是滑动窗口的问题&#xff0c;更重要的问题是在循环控制中&#xff0c;不恰当的语法使用会导致这道题出现很严重的问题&#xff0c;这导致我做这道题做了很多天&…

亿级数据毫秒级响应?

作为一名深陷在增删改查泥潭中练习时长三年的夹娃练习生&#xff0c;偶尔会因为没有开发任务不知道周报写什么而苦恼。 正愁这周写啥呢&#xff0c;组长过来交代了个跟进第三方公司性能测试报告的工作&#xff0c;我一寻思这活不最好干了吗&#xff0c;正愁不知道周报咋写呢&a…

github上传文件及其问题解决

文章目录 1. github上上传文件夹2. <filename> does not have a commit checked out3. this exceeds GitHubs file size limit of 100.00 MB4. error: src refspec master does not match any 1. github上上传文件夹 首先在github上create a new repository&#xff0c;…

C语言王国探险记之字符串+注释

王国探险记系列 文章目录&#xff08;3&#xff09; 前言 一&#xff0c;什么是字符串呢&#xff1f; 1&#xff0c;那C语言是怎么表示字符串的呢? "hello world.\n" 2&#xff0c;证明字符串的结束标志是一个 \0 的转义字符 3&#xff0c;证明字符串的结束标…

云原生之深入解析Flink on k8s的运行模式与实战操作

一、概述 Flink 核心是一个流式的数据流执行引擎&#xff0c;并且能够基于同一个 Flink 运行时&#xff0c;提供支持流处理和批处理两种类型应用。其针对数据流的分布式计算提供了数据分布&#xff0c;数据通信及容错机制等功能。Flink 官网不同版本的文档flink on k8s 官方文…

linux-2.6.22.6内核网卡驱动框架分析

网络协议分为很多层&#xff0c;而驱动这层对应于实际的物理网卡部分&#xff0c;这也是最底层的部分&#xff0c;以cs89x0.c这个驱动程序为例来分析下网卡驱动程序框架。 正常开发一个驱动程序时&#xff0c;一般都遵循以下几个步骤&#xff1a; 1.分配某个结构体 2.设置该结…

IDEA将java项目打包为jar包

方法 首先在src -> resources目录下建立一个文件夹&#xff0c;然后再在新建文件夹里面建立META-INF文件夹&#xff08;不推荐直接建立META-INF&#xff0c;否则后面打包完的jar包需要手动修改配置&#xff09; 然后点击File -> Project Structure -> Artifacts -&g…

第三章:Faster R-CNN网络详解(《Faster R-CNN: 基于区域提议网络的实时目标检测》)

(目标检测篇&#xff09;系列文章目录 第一章:R-CNN网络详解 第二章:Fast R-CNN网络详解 第三章:Faster R-CNN网络详解 第四章:YOLO v1网络详解 第五章:YOLO v2网络详解 第六章:YOLO v3网络详解 文章目录 系列文章目录技术干货集锦前言一、摘要二、正文分析 1.引入库2.读…

Mysql的逻辑架构_读写锁_事物

概览 一. MySql的逻辑架构1. 逻辑架构图2. 连接管理与安全性 二. 并发控制1. 读写锁2. 锁粒度 三. 事务1. 特性2. 隔离级别3. 死锁4. 事物日志&#xff1f;5.MySql中的事物 mysql最与众不同的特性&#xff1a;存储引擎架构 架构的设计&#xff1a; 将查询处理(Query Processin…

7、注解与自定义注解

1 注解 注解很厉害&#xff0c;它可以增强我们的java代码&#xff0c;同时利用反射技术可以扩充实现很多功能。它们被广泛应用于三大框架底层。 传统我们通过xml文本文件声明方式(如下图,但是XML比较繁琐且不易检查)&#xff0c;而现在最主流的开发都是基于注解方式&#xff0c…

房贷计算器——新增选择还款方式

房贷计算器——新增选择还款方式 #!/usr/bin/env python # coding: utf-8# In[4]: 文字‘房贷计算器’ 文字‘贷款总金额’&#xff1a;输入框 文字‘贷款期限’&#xff1a;输入框 文字‘年利率’&#xff1a;输入框 按钮‘开始计算’ 返回&#xff1a; 月供 总利息 from tki…

【Framework】bindService启动流程

前言 在【Service启动流程之startService】 中&#xff0c;我们已经分析了startService的流程&#xff0c;这篇就继续讲bindService的流程&#xff0c;他们两有很多相似之处。同样&#xff0c;流程图在总结处。 我们在调用bindService方法时候&#xff0c;实际调用的是Contex…

台庆|三联开关怎么接线?

三联开关是一种常见的开关类型&#xff0c;通常用于控制一个电路中的三个不同的电器或灯具。它的用途非常广泛&#xff0c;因此了解如何正确接线是非常重要的。在本文中&#xff0c;我们将详细讨论三联开关的接线方法。 我们先来看看三联开关实物图与线路图&#xff1a; 接下来…

【音视频处理】FFmpeg详解,命令行、源码、编译安装

大家好&#xff0c;欢迎来到停止重构的频道。 本期我们讨论FFmpeg。 这里先提一个问题&#xff0c;FFmpeg命令行功能如此强大&#xff0c;为什么还需要舍近求远地调用库函数呢 &#xff1f; 我们按这样的顺序讨论 &#xff1a; 1、 FFmpeg命令行说明 2、 FFmpeg代码结构…

如何在 JavaScript 中压缩字符串

在 JavaScript 中&#xff0c;可以有范围很广的压缩&#xff0c;比如 gzip 之类的文件压缩等等。 在这里&#xff0c;我们将讨论两种压缩字符串的方法。 最初&#xff0c;我们将重点介绍霍夫曼算法。 稍后&#xff0c;我们将介绍解决任务的 LZString 方法。 在 JavaScript 中使…

主成分分析系列(一)概览及数据为何要中心化

一、概览 主成分分析&#xff08;Principle Component Analysis&#xff0c;PCA&#xff09;算法属于数据降维算法里面的一种。数据降维算法的主要想法是从高维度数据中找到一种结构&#xff0c;这种结构蕴含了数据中的大部分信息&#xff0c;从而将高维数据降维到低维数据&am…

Apikit 自学日记:参数构造器

构造器是测试时系统提供的快速生成请求数据的工具。一般用于快速对数据进行加密和生成随机数值。可在请求参数中某个字段的右侧选择构造器操作&#xff0c;通过构造器生成该字段的参数值。构造器由两种类型的操作组成&#xff1a;设置初始数据和多重操作。 设置初始数据 其中初…