C++类与对象进阶知识深度解析
目录
一、再谈构造函数
(一)构造函数体赋值
(二)初始化列表
(三)成员变量初始化顺序
(四)explicit关键字
二、static成员
(一)概念
(二)特性
(三)相关问题思考
三、友元
(一)友元函数
(二)友元类
一、再谈构造函数
(一)构造函数体赋值
在C++中,创建对象时,编译器会调用构造函数为对象的成员变量赋初始值。以 Date 类为例:
cppclass Date{public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;};
这里看似是初始化,实则是赋值。真正的初始化只能进行一次,而构造函数体内可以多次赋值。比如先创建对象,再多次调用成员函数修改成员变量的值。
(二)初始化列表
初始化列表以冒号开始,后面跟着逗号分隔的成员变量初始化表达式。还是以 Date 类为例:
cppclass Date{public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}private:int _year;int _month;int _day;};
使用初始化列表有诸多好处。首先,对于一些成员变量,如引用成员变量、 const 成员变量以及没有默认构造函数的自定义类型成员变量,必须使用初始化列表进行初始化。例如:
cppclass A{public:A(int a) : _a(a) {}private:int _a;};class B{public:B(int a, int& ref): _aobj(a), _ref(ref), _n(10){}private:A _aobj; // 没有默认构造函数int& _ref; // 引用const int _n; // const};
其次,使用初始化列表效率更高。因为对于自定义类型成员变量,即使在构造函数体中赋值,编译器也会先使用默认构造函数初始化,再进行赋值操作,而初始化列表可以直接调用合适的构造函数初始化。
(三)成员变量初始化顺序
成员变量在类中声明的次序就是其在初始化列表中的初始化顺序,与在初始化列表中书写的先后次序无关。例如:
cppclass A{public:A(int a): _a1(a), _a2(_a1){}void Print() {std::cout << _a1 << " " << _a2 << std::endl;}private:int _a2;int _a1;};int main() {A aa(1);aa.Print();return 0;}
这里先初始化 _a2 ,再初始化 _a1 ,所以输出结果可能不是预期的 1 1 ,而是 1 随机值 。
(四)explicit关键字
对于接收单个参数的构造函数,除了构造对象外,还具有类型转换的作用。但有时候我们不希望这种隐式类型转换发生,就可以使用 explicit 关键字。比如 Date 类:
cppclass Date{public:// 单参构造函数,没有使用explicit修饰,具有类型转换作用// explicit修饰构造函数,禁止类型转换,去掉explicit后,代码可以通过编译explicit Date(int year):_year(year){}// 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具有类型转换作用// explicit修饰构造函数,禁止类型转换explicit Date(int year, int month = 1, int day = 1): _year(year), _month(month), _day(day){}Date& operator=(const Date& d){if (this!= &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}private:int _year;int _month;int _day;};void Test(){Date d1(2022);// 用一个整形变量给日期类型对象赋值// 实际编译器背后会用2023构造一个无名对象,最后用无名对象给d1对象进行赋值d1 = 2023; // 将1屏蔽掉,放开时则编译失败,因为explicit修饰构造函数,禁止了单参构造函数类型转换的作用}
使用 explicit 修饰后,就禁止了构造函数的隐式类型转换,提高了代码的可读性和安全性。
二、static成员
(一)概念
在C++中,用 static 修饰的类成员称为类的静态成员。用 static 修饰的成员变量称为静态成员变量,用 static 修饰的成员函数称为静态成员函数。静态成员变量一定要在类外进行初始化。
例如,要实现一个类来计算程序中创建了多少个类对象:
cppclass A{public:A() { ++_scount; }A(const A& t) { ++_scount; }~A() { --_scount; }static int GetACount() { return _scount; }private:static int _scount;};int A::_scount = 0;void TestA(){std::cout << A::GetACount() << std::endl;A a1, a2;A a3(a1);std::cout << A::GetACount() << std::endl;}
这里 _scount 是静态成员变量,记录对象个数, GetACount 是静态成员函数,用于获取对象个数。
(二)特性
1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区。
2. 静态成员变量必须在类外定义,定义时不添加 static 关键字,类中只是声明。
3. 类静态成员既可用 类名::静态成员 或者 对象.静态成员 来访问。
4. 静态成员函数没有隐藏的 this 指针,不能访问任何非静态成员。
5. 静态成员也是类的成员,受 public 、 protected 、 private 访问限定符的限制。
(三)相关问题思考
1. 静态成员函数可以调用非静态成员函数吗?
答案是否定的。因为静态成员函数没有 this 指针,无法确定要操作哪个具体对象的非静态成员。
2. 非静态成员函数可以调用类的静态成员函数吗?
可以。因为静态成员函数是类共享的,非静态成员函数有 this 指针,可以通过类名或对象来调用静态成员函数。
三、友元
友元提供了一种突破封装的方式,在某些情况下能带来便利,但也会增加耦合度,破坏封装性,所以不宜多用。友元分为友元函数和友元类。
(一)友元函数
当我们尝试重载 operator<< 输出运算符时,会发现无法将其重载成成员函数。因为 cout 的输出流对象和隐含的 this 指针在抢占第一个参数的位置, this 指针默认是第一个参数也就是左操作数了,但实际使用中 cout 需要是第一个形参对象才能正常使用。所以要将 operator<< 重载成全局函数,但这样又导致类外没办法访问类的私有成员,此时就需要友元来解决。 operator>> 同理。
以 Date 类为例:
cppclass Date{public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}// d1 << cout; -> d1.operator<<(&d1, cout); 不符合常规调用// 因为成员函数第一个参数一定是隐藏的this,所以d1必须放在<<的左侧friend std::ostream& operator<<(std::ostream& _cout, const Date& d);friend std::istream& operator>>(std::istream& _cin, Date& d);private:int _year;int _month;int _day;};std::ostream& operator<<(std::ostream& _cout, const Date& d){_cout << d._year << "-" << d._month << "-" << d._day;return _cout;}std::istream& operator>>(std::istream& _cin, Date& d){_cin >> d._year;_cin >> d._month;_cin >> d._day;return _cin;}int main(){Date d;std::cin >> d;std::cout << d << std::endl;return 0;}
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明。友元函数具有以下特点:
- 友元函数可访问类的私有和保护成员,但不是类的成员函数。
- 友元函数不能用 const 修饰。
- 友元函数可以在类定义的任何地方声明,不受类访问限定符限制。
- 一个函数可以是多个类的友元函数。
- 友元函数的调用与普通函数的调用原理相同。
(二)友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。例如:
cppclass Time{friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量public:Time(int hour = 0, int minute = 0, int second = 0): _hour(hour), _minute(minute), _second(second){}private:int _hour;int _minute;int _second;};class Date{public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}void SetTimeOfDate(int hour, int minute, int second){// 直接访问时间类私有的成员变量_t._hour = hour;_t._minute = minute;_t._second = second;}private:int _year;int _month;int _day;Time _t;};
友元类有以下特性:
- 友元关系是单向的,不具有交换性。比如上述 Time 类和 Date 类,在 Time 类中声明 Date 类为其友元类,那么可以在 Date 类中直接访问 Time 类的私有成员变量,但想在 Time 类中访问 Date 类中私有的成员变量则不行。
- 友元关系不能传递。如果 B 是 A 的友元, C 是 B 的友元,则不能说明 C 是 A 的友元。
- 友元关系不能继承,在继承位置再给大家详细介绍。
通过对构造函数、 static 成员以及友元的深入剖析,我们对C++类与对象的知识有了更全面和深刻的理解。在实际编程中,合理运用这些特性可以编写出更高效、灵活且安全的代码。
相关文章:
C++类与对象进阶知识深度解析
目录 一、再谈构造函数 (一)构造函数体赋值 (二)初始化列表 (三)成员变量初始化顺序 (四)explicit关键字 二、static成员 (一)概念 (二&am…...
BoostSearch搜索引擎项目 —— 测试用例设计 + web自动化测试代码
web自动化代码: https://gitee.com/chicken-c/boost-search/tree/master/AutoTest...
【Ansible自动化运维】一、初步了解,开启自动化运维之旅
在当今数字化时代,随着企业 IT 基础设施规模的不断扩大,传统的手工运维方式逐渐显得力不从心。自动化运维技术应运而生,其中 Ansible 凭借其简洁易用、功能强大的特点,成为众多运维工程师和开发人员的首选工具。本篇文章将从基础概…...
AI日报 - 2025年4月9日
🌟 今日概览(60秒速览) ▎🤖 AGI突破 | DeepSeek AI推出自我原则批判调优(SPCT)新方法 通过GRMs自我创建和批判原则,性能媲美671B参数大模型 ▎💼 商业动向 | NVIDIA发布Llama-Nemotron-Ultra 253B模型 开放权重和训练数据&#x…...
2025年二级建造师考前冲刺题库
二建考前冲刺练习通常会涵盖考试的重点和高频考点,考生在做题过程中可以加深对这些知识点的理解和记忆,提高对重点知识的掌握程度。 建设工程法规及相关知识 1、单选题:关于建设工程中代理的说法,正确的是( …...
蓝桥·20264-祝福语--找连续字串的长度
#include <iostream> using namespace std; int main() {// 请在此输入您的代码//最小字典序,一定是全a,找s的最长字串a,结果就是该字串长度加1(t不能是s的子串)//所以这道题就变成了,找s中字串a出现的长度strin…...
条件概率、概率乘法公式、全概率公式和贝叶斯 (Bayes) 公式
定义 设 P ( A ) > 0 P(A) > 0 P(A)>0,若在随机事件 A A A发生的条件下随机事件 B B B发生的概率记作 P ( B ∣ A ) P(B|A) P(B∣A),定义 P ( B ∣ A ) P ( A B ) P ( A ) P(B|A) \frac{P(AB)}{P(A)} P(B∣A)P(A)P(AB) 则称 P ( B ∣ A ) …...
pdf转latex
Doc2X(https://doc2x.noedgeai.com/) Doc2X 是一个由 NoEdgeAI 提供的在线工具,主要用于将 PDF 文件(尤其是学术论文、报告等文档)转换为 LaTeX 格式。LaTeX 是一种高质量排版系统,广泛应用于学术界和出版…...
【Unity】Unity Transform缩放控制教程:实现3D模型缩放交互,支持按钮/鼠标/手势操作
【Unity 】Transform缩放控制教程:实现3D模型缩放交互,支持按钮/鼠标/手势操作 在Unity开发中,Transform组件承担着场景中物体的空间信息控制,包括位置、旋转和缩放。而缩放(Scale)操作,作为三…...
【Linux篇】缓冲区的工作原理:如何影响你程序的输入输出速度
从内存到磁盘:缓冲区如何提升文件I/O效率 一. 缓冲区1.1 什么是缓冲区1.2 为什么要引入缓冲区1.3 缓冲区类型1.4 FILE1.4.1 基本概念1.4.2 FILE 结构体的作用1.4.3 FILE 的工作机制 二. 最后 在程序开发中,缓冲区是一个经常被提及却不容易深入理解的概念…...
kotlin,Android,jetpack compose,日期时间设置
AI生成,调试出来学习,这些小组件会用了,就可以组合一个大点的程序了。 package com.example.mydatetimeimport android.app.AlertDialog import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.co…...
ASP.NET图书馆借阅系统(源码+lw+部署文档+讲解),源码可白嫖!
摘要 近些年来,随着科技的飞速发展,互联网的普及逐渐延伸到各行各业中,给人们生活带来了十分的便利,图书馆借阅系统利用计算机网络实现信息化管理,使图书信息、图书借阅、归还的管理发展和服务水平有显著提升。 本文拟…...
LeetCode算法题(Go语言实现)_35
题目 给你一棵根为 root 的二叉树,请你返回二叉树中好节点的数目。 「好节点」X 定义为:从根到该节点 X 所经过的节点中,没有任何节点的值大于 X 的值。 一、代码实现 func goodNodes(root *TreeNode) int {if root nil {return 0}return d…...
vi/vim常用快捷键
那么今天我们继续昨天没有介绍完的vi编辑器,来看看常用的一些快捷键,方便我们对文件的编辑. 1.拷贝当前行yy,拷贝当前行向下的5行5yy,并粘贴(输入p) 2.删除当前行dd,删除当前行向下的5行5d 3.在文件中查找某个单词[命令模式/关键字,回车查找,输入n就是查找下一个] ⭐️&…...
JVM核心机制:类加载×字节码引擎×垃圾回收机制
🚀前言 “为什么你的Spring应用启动慢?为什么GC总是突然卡顿?答案藏在JVM的核心机制里! 本文将用全流程图解字节码案例,带你穿透三大核心机制: 类加载:双亲委派如何防止恶意代码入侵ÿ…...
opencv无法设置禁用RGB转换问题
树莓派连接摄像头,摄像头输出格式为YUYV(YUV422)。 通过执行 v4l2-ctl --list-formats --device/dev/video0 可以看的具体的摄像头的数据格式。 使用opencv获取视频流,通过cap.set(cv2.CAP_PROP_CONVERT_RGB, 0)设置禁用自动转换RGB格式,但是打印输出…...
k8s 1.30.6版本部署(使用canal插件)
#系统环境准备 参考 https://blog.csdn.net/dingzy1/article/details/147062698?spm1001.2014.3001.5501 #配置下载源 curl -fsSL https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/deb/Release.key |gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyri…...
GZ036区块链卷一 EtherStore合约漏洞详解
题目 pragma solidity >0.8.3;contract EtherStore {mapping(address > uint) public balances;function deposit() public payable {balances[msg.sender] msg.value;emit Balance(balances[msg.sender]);}function withdraw() public {uint bal balances[msg.sender…...
MCP+Blender创建电力塔
MCP(Model Context Protocol)与Blender的结合是当前AI与3D建模领域的热门技术,它通过协议化的方式让Claude等AI模型直接控制Blender,实现自动化3D建模。 1. 功能与原理 • 核心能力:用户通过自然语言指令(…...
什么是RACI矩阵,应用在什么场景?
一、什么是RACI RACI矩阵是一种用于明确项目或任务中角色与责任的管理工具,通过定义不同人员在任务中的参与程度来避免职责不清的问题。以下是其核心要点: RACI的含义 ● R(Responsible)执行者:直接完成任务…...
Selenium自动化:玩转浏览器,搞定动态页面爬取
嘿,各位爬虫爱好者和自动化达人们!是不是经常遇到这种情况:信心满满地写好爬虫,requests一把梭,结果抓下来的HTML里,想要的数据空空如也?定睛一看,原来数据是靠JavaScript动态加载出…...
QAI AppBuilder 快速上手(8): 图像修复应用实例2
LaMa-Dilated模型旨在通过扩张卷积技术实现高效的图像擦除和修复。该模型采用先进的卷积神经网络架构,能够处理复杂的图像输入,并填补图像中的缺失部分,使修复后的图像更加自然和逼真。LaMa-Dilated不仅在图像编辑领域表现出色,还…...
`ConstantPositionProperty` 的使用与应用
ConstantPositionProperty 的使用与应用 1. 什么是 ConstantPositionProperty? ConstantPositionProperty 是 Cesium 中用于表示实体位置的属性类。它表示一个实体在三维空间中的位置是固定的,不会随时间变化。与动态位置属性(如 SampledPo…...
【计网】作业4
一. 单选题(共22题,64分) 1. (单选题)主机甲采用停止-等待协议向主机乙发送数据,数据传输速率是4kb/s,单向传播时延为30ms,忽略确认帧的发送时延。当信道利用率等于80%时,数据帧的长度为&#…...
MPDrive:利用基于标记的提示学习提高自动驾驶的空间理解能力
25年4月来自南方科技大学、百度、英国 KCL和琶洲实验室(广东 AI 和数字经济实验室)的论文“MPDrive: Improving Spatial Understanding with Marker-Based Prompt Learning for Autonomous Driving”。 自动驾驶视觉问答(AD-VQA)…...
QTSql全解析:从连接到查询的数据库集成指南
概览 与数据库的有效集成是确保数据管理效率和应用性能的关键,Qt框架就提供了强大的QtSql模块,使得开发者能够轻松地进行数据库操作,包括连接、查询执行以及结果处理等 一、引入QtSql模块 首先,需要在项目中引入QtSql模块&…...
FreeRTOS临界区
在FreeRTOS中,临界区通过关闭可管理的中断来保护共享资源,具体关闭的中断层级由configMAX_SYSCALL_INTERRUPT_PRIORITY宏定义决定。以下是关键点解析: 中断优先级分类: 高优先级中断:数值低于configMAX_SYSCALL_INTERR…...
【学习笔记】HTTP和HTTPS的核心区别及工作原理
一、基础概念 HTTP(超文本传输协议):明文传输数据,默认端口80,容易被窃听或篡改。 HTTPS(HTTP SSL/TLS):通过加密传输数据,默认端口443,保障安全性。 二、…...
Dubbo的简单介绍
Dubbo的简单介绍 Dubbo 是一个高性能的 Java RPC 框架,最初由阿里巴巴开发,用于构建分布式服务。它主要用于提供服务间的通信,支持高效的远程调用和服务治理,常用于大规模分布式系统中。Dubbo 提供了以下几个核心功能:…...
7.2 重复推送(每日、每周等)
1. 核心方法 使用 UNCalendarNotificationTrigger 的 dateMatching 参数配置日历组件(DateComponents),结合 repeats: true 实现周期性触发。 2. 不同频率的重复推送配置 2.1 每日重复 每天固定时间触发(如上午 10:00ÿ…...
