【C++精华铺】6.C++类和对象(下)类与对象的知识补充及编译器优化

目录

1. 再谈构造

1.1 成员变量的初始化(初始化列表)

1.2 初始化列表的行为

1.3 explicit关键字

 2. 类中的static成员

2.1 静态成员变量

2.2 静态成员函数

3. 友元

3.1 友元函数

3.1 友元类

4. 内部类

 5. 匿名对象

 6. 对象拷贝时候的编译器优化

 

1. 再谈构造

1.1 成员变量的初始化(初始化列表)

        为什么还要去看初始化的问题呢,因为这里有一个比较大的误区,我们都知道创建对象的时候会调用构造函数对成员进行初始化,所以我们会把下面的代码看作初始化,但其实下面的构造函数代码只能叫做赋值。

class Date
{
public:Date(int year,int month,int day){_year = year;   //因为初始化只有一次,_month = month; //而在函数里面可以多次赋值,不能叫做初始化_day = day;}
private:int _year;int _month;int _day;
};

        而真正的初始化是在初始化列表中进行的,初始化列表的位置是在构造函数”{}“的前面,是由一个冒号后面跟着多个用逗号隔开的成员变量,每个成员变量后面都有括号,括号里面就是初始值,初始化的次序与成员列表次序无关,是按照成员的声明顺序进行初始化,如下:

	Date(int year, int month, int day):_year(year),_month(month),_day(day){}

        而且像下面三种成员必须使用初始化列表进行初始化:

  1. const 成员变量:因为const成员具有常性不能进行赋值,而函数体是进行赋值的,所以只能在初始化列表进行初始化(具体与初始化列表的行为有关,待会叙述
  2. 引用成员变量:因为引用在定义的时候必须初始化,并且引用的实体不能更改,如果在函数体里进行,本质上是一个赋值重载。
  3. 没有默认构造的自定义类型:因为自定义类型会调用默认构造进行初始化,如果没有构造就必须显示的初始化。

1.2 初始化列表的行为

        我想大家应该都很困惑,为什么在初始化列表里面是初始化而在函数体里面就是赋值?初始化列表的执行时间是在函数体之前的,当我们调用构造函数的时候,编译器会首先去按照声明顺序进行默认的初始化,如果在初始化列表中有构造初始值则不会执行默认初始化,而去执行显式的初始化,所以无论我们有没有去写初始化列表,都是会在函数体执行之前初始化,这也就解释了为什么有些成员只能在初始化列表里面进行初始化和初始化列表的顺序与初始化顺序无关。而初始化列表对于编译器只是一个参考,可以认为编译器在执行的时候如果你(列表)有,我就按你的来,如果没有我就按照我自己的来。而const成员一旦执行了初始化就不能进行赋值,所以在语法上规定了const必须人为的给构造初始值,这也就是为什么const必须在初始化列表进行初始化了。

        而当我们执行到函数体的时候所有成员其实早就执行默认初始化初始化完成了,这个时候去进行所谓的”初始化“其实都是赋值(拷贝或者进行赋值重载)。

        无论什么时候我们都建议使用初始化列表进行初始化,首先就是不容易出错,其次就是规则上的统一。

1.3 explicit关键字

        如果构造函数只有单个参数或者除第一个参数无默认值其余均有默认值,在一些情况下会存在隐式类型转换。比如

class A
{
public:A(int a):_a(a){}
private:int _a;
};
//
class B
{
public:B(int a, int b = 1, int c = 1):_a(a),_b(b),_c(c){}
private:int _a;int _b;int _c;
};int main()
{A a1 = 1;  //这俩种情况都会发生隐式类型转换 转换成一个无名类型的对象进行拷贝B b1 = 1;//C++11B b2 = { 1, 2, 3}; //c++11支持的括号初始化,将数组进行类型转换再构造,            //想了解可以看我的c++11专栏}

23c9ecf132be4e57b0a78ea0b99255ae.png

         对于代码中提到的括号初始化可以看我的这一篇文章:【C++11】 统一的列表初始化( {}初始化 )_子亦半截诗的博客-CSDN博客

        回归正题,上述的这种类型转换有的时候我们并不希望发生,所以就有了explicit关键字,用这个关键字修饰构造函数,将不会发生这种类型转换,同时C++11的括号初始化特性也会被禁用。

class A
{
public:explicit A(int a):_a(a){}
private:int _a;
};
//
class B
{
public:explicit B(int a, int b = 1, int c = 1):_a(a), _b(b), _c(c){}
private:int _a;int _b;int _c;
};
int main()
{A a1 = 1;  //报错:E0415	不存在从 "int" 转换到 "A" 的适当构造函数B b1 = 1;  //报错:E0415	不存在从 "int" 转换到 "B" 的适当构造函数//C++11B b2 = { 1, 2, 3 };   //报错:E2334	复制列表初始化不能使用标记为“显式”的构造函数
}

 报错:c5bc0a3024c047f48e01265fc3cd5e8f.png

 

 2. 类中的static成员

        类中的静态成员和类中其他的成员有很大的区别,非静态成员变量的初始化在初始化列表里面进行,但是静态成员变量不能在初始化列表中初始化,因为static成员并不属于某个对象,static成员被存放在静态区,被同类型的所有对象共享

2.1 静态成员变量

        静态成员变量属于同类型的所有对象,它的定义和初始化在类外进行。在定义的时候需要在变量名前面加上 类名:: 。

int A::_a = 0;
class A
{
public:
private:static int _a;
};

2.2 静态成员函数

        由于静态函数属于每一个类对象,而不属于某个对象,所以静态成员函数也就没有this指针,所以也就不能访问类的非静态成员。非静态成员函数可以调用静态成员函数,但是静态成员函数不能调用非静态函数。可能有人要疑问了:非静态成员函数不也是被共享的吗,为什么不能被静态成员函数访问呢?这是因为非静态成员函数需要传入this指针,而静态成员函数是没有this指针的。

int A::_a = 0;
class A
{
public:static void test() {  }void test1() { test(); }
private:static int _a;
};

3. 友元

        友元是一种突破封装的方式,让外部成员访问自己私有成员的一种方式,但过度使用会破坏封装。

3.1 友元函数

        友元函数的声明就是在类中声明函数时在前面加上friend关键字,声明成友元函数这个函数就能访问对象的私有成员。在一些特殊情况下需要使用,就比如”<<“流写入符号重载,由于"<<"的左操作数必须是ostream对象,所以第一个参数就必须是ostream类型,但是如果定义成成员函数,第一个参数就变成this指针了,这会导致我们使用”<<“的时候就变成” 对象<<cout  “,这与我们的习惯就不符,所以流写入的符号重载必须定义成全局函数,而且还必须要能访问类的私有成员,这个时候友元函数就可以发挥作用了。如下:

class Date
{friend ostream& operator<<(ostream& _cout, const Date& d);friend istream& operator>>(istream& _cin, Date& d);
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << ' ' << d._month << ' ' << d._day << endl;return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{_cin >> d._year;_cin >> d._month;_cin >> d._day;return _cin;
}
  •  友元函数可以访问类的所有成员,但不是类的成员
  • 友元函数不可以用const修饰
  • 友元函数不受访问限定符限制

3.1 友元类

        如果一个类A声明了一个友元类B,那么B类中的所有成员都可以访问A类的所有成员,但A类不能访问B类的成员。友元关系不能传递和继承(继承以后再说),比如C是D的友元,D是E的友元,但C不是E的友元。

class A
{friend class B;
private:int _aa;int _ab;int _ac;
};
class B
{void print(){cout << _atest._aa << ' ' << _atest._ab << ' ' << _atest._ac << endl;}private:A _atest;
};

4. 内部类

        当一个类定义在另一个类的内部,这个类就被称之为内部类。内部类相当于外部类的友元类,可以通过对象参数访问内部类中的所有成员,访问静态成员的时候不需要对象参数就可以直接访问,但是外部类不能访问内部类的成员。内部类是一个独立的类,对外部类的大小没有任何影响。外部成员对内部类的访问会受到访问限定修饰符的限制。

class A
{
public:class B{void print(const A& a){cout << a._a;}};
private:int _a;
};

 5. 匿名对象

        匿名对象的定义就是在对象后面加上一个括号,匿名对象不用取名字,声明周期也只有一行,使用完后就自动调用析构函数销毁了。

class A
{
public:static int _a;
};
int A::_a = 0;int main()
{A();   //匿名对象cout << A()._a;
}

 6. 对象拷贝时候的编译器优化

        在我们传参或者传值返回的时候可能会进行频繁的构造和拷贝构造,如果对象较大,会造成极大的资源消耗,所以为此编译器做出一些优化。(只针对构造,赋值重载不会进行优化)

class A
{
public:A(int a = 0){cout << "A(int a = 0)" << endl;}A(const A& aa){cout << "A(const A & aa)" << endl;}~A(){cout << "~A()" << endl;}
};
void f1(A a)
{}
A f2()
{A a;return a;
}
int main()
{A a;f1(1);  //连续构造+拷贝构造->直接构造f1(A(2)); //连续构造+拷贝构造->直接构造A a1 = f2(); //拷贝构造+拷贝构造->优化为一个拷贝构造
}

a385f1784f714926b72221a51c924dcd.png

 

 

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

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

相关文章

GitHub 打不开解决方案

GitHub 这几年国内普通用户越来越难以访问&#xff0c;github 作为全球最大的开源平台&#xff0c;里面有用的内容很多&#xff0c;不管是对专业用户还是普通用户&#xff0c;无法访问都是很严重的问题。 1.GitHub 加速镜像 kgithub 是一个公益加速项目&#xff0c;仅需在 gi…

【LeetCode每日一题】——41.缺失的第一个正数

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 哈希表 二【题目难度】 困难 三【题目编号】 41.缺失的第一个正数 四【题目描述】 给你一个…

Compute shader SV 理解图

本图转子&#xff1a;【Computeshader】个人总结_蒋伟博的博客-CSDN博客

【Rust】Rust学习 第十二章一个 I/O 项目:构建一个命令行程序

本章既是一个目前所学的很多技能的概括&#xff0c;也是一个更多标准库功能的探索。我们将构建一个与文件和命令行输入/输出交互的命令行工具来练习现在一些你已经掌握的 Rust 技能。 Rust 的运行速度、安全性、单二进制文件输出和跨平台支持使其成为创建命令行程序的绝佳选择…

谈一谈在两个商业项目中使用MVI架构后的感悟

作者&#xff1a;leobertlan 前言 当时项目采用MVP分层设计&#xff0c;组员的代码风格差异也较大&#xff0c;代码中类职责赋予与封装风格各成一套&#xff0c;随着业务急速膨胀&#xff0c;代码越发混乱。试图用 MVI架构 单向流 形成 掣肘 带来一致风格。 但这种做法不够以…

linux系列基本介绍

虽然我们常说Linux操作系统&#xff0c;这种叫法是不正确的&#xff0c;严格意义上讲&#xff0c;Linux并不是操作系统&#xff0c;而是属于操作系统的一个内核&#xff0c;inux内核提供了操作系统的核心功能&#xff0c;如进程管理、内存管理、文件系统等。 Linux有很多不同的…

LeetCode 热题 100 JavaScript--33. 搜索旋转排序数组

整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nums[1], …,…

yolov5 转换为rknn模型在3588上运行

为了把yolov5在rk3588上跑起来&#xff0c;在网上搜罗了一圈,踩了一些坑。由于瑞芯微的文档有升级&#xff0c;导致和网络的文章有出入&#xff0c;所以做个记录。 rknn-toolkit 转换文档&#xff1a; 瑞芯微的转换文档在 rknn-toolkit/example/pytorch/yolov5/REAME.md 里 …

LangChain入门:构建LLM驱动的应用程序的初学者指南

LangChain & DemoGPT 一、介绍 你有没有想过如何使用大型语言模型&#xff08;LLM&#xff09;构建强大的应用程序&#xff1f;或者&#xff0c;也许您正在寻找一种简化的方式来开发这些应用程序&#xff1f;那么你来对地方了&#xff01;本指南将向您介绍LangChain&#x…

【Sklearn】基于逻辑回归算法的数据分类预测(Excel可直接替换数据)

【Sklearn】基于逻辑回归算法的数据分类预测(Excel可直接替换数据) 1.模型原理2.模型参数3.文件结构4.Excel数据5.下载地址6.完整代码7.运行结果1.模型原理 逻辑回归是一种用于二分类问题的统计学习方法,尽管名字中含有“回归”,但实际上是一种分类算法。它的基本原理是通…

网络基础--ARP协议介绍

1、ARP作用 ARP&#xff08; Address Resolution Protocol&#xff0c;地址解析协议&#xff09;是将 IP 地址解析为以太网 MAC 地址&#xff08;或称物理地址&#xff09;的协议。在局域网中&#xff0c;当主机或其它网络设备有数据要发送给另一个主机或设备时&#xff0c;它必…

Java鹰眼轨迹服务 轻骑小程序 运动健康与社交案例

Java地图专题课 基本API BMapGLLib 地图找房案例 MongoDB 百度地图鹰眼轨迹服务 鹰眼轨迹服务概述 鹰眼是一套轨迹管理服务&#xff0c;提供各端SDK和API供开发者便捷接入&#xff0c;追踪所管理的车辆/人员等运动物体。 基于鹰眼提供的接口和云端服务&#xff0c;开发者可以迅…

前后端分离------后端创建笔记(05)用户列表查询接口(下)

本文章转载于【SpringBootVue】全网最简单但实用的前后端分离项目实战笔记 - 前端_大菜007的博客-CSDN博客 仅用于学习和讨论&#xff0c;如有侵权请联系 源码&#xff1a;https://gitee.com/green_vegetables/x-admin-project.git 素材&#xff1a;https://pan.baidu.com/s/…

Java通过文件流和文件地址下载文件

通过文件流下载文件 如何使用 MultipartFile 进行文件上传、下载到本地&#xff0c;并返回保存路径呢&#xff1a; import org.springframework.web.multipart.MultipartFile;import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.IOExcep…

Redis_缓存2_缓存删除和淘汰策略

14.5 缓存数据的删除和替换 14.5.1 过期数据 可以使用ttl查看key的状态。已过期的数据&#xff0c;redis并未马上删除。优先去执行读写数据操作&#xff0c;删除操作延后执行。 14.5.2 删除策略 redis中每一个value对应一个内存地址&#xff0c;在expires&#xff0c;一个内…

BC117 小乐乐走台阶(附完整代码)

描述 小乐乐上课需要走n阶台阶&#xff0c;因为他腿比较长&#xff0c;所以每次可以选择走一阶或者走两阶&#xff0c;那么他一共有多少种走法&#xff1f; 输入描述 输入包含一个整数n (1 ≤ n ≤ 30) 输出描述 输出一个整数&#xff0c;即小乐乐可以走的方法数。 思路&a…

分享个试卷去笔迹什么软件,几个步骤轻松擦除

试卷擦去笔迹是一项非常关键的技能&#xff0c;它可以帮助你更好地管理你的笔记和文件。不管是小伙伴们想重新测试试卷或者是将试卷输出为电子版&#xff0c;都可以实现的。在这篇文章中&#xff0c;我将分享一些方法和软件&#xff0c;帮助你更好地进行试卷擦除。有需要的小伙…

个人博客系统测试报告

文章目录 一、功能测试1.编写测试用例2.总结测试后发现的BUG 二、UI自动化测试0.搭建测试环境1. 创建公共类2.注册页面UI自动化测试用例编写3.登录页面UI自动化测试用例编写4.用户博客列表页面自动化测试5. 修改个信息页面6. 文章编辑页面7. 设置密保问题发现bug 8. 所有用户文…

Stable Diffusion +EbSynth应用实践和经验分享

Ebsynth应用 1.安装ffmpeg 2.安装pip install transparent-background,下载模型https://www.mediafire.com/file/gjvux7ys4to9b4v/latest.pth/file 放到C:\Users\自己的用户名.transparent-background\加一个ckpt_base.pth文件 3.秋叶安装ebsynth插件,重启webui 填写项目基本…

Redis 持久化及集群架构

Redis 持久化及集群架构 本篇技术博文将深入探讨 Redis 持久化机制的原理、配置和使用方式。我们将介绍两种常用的持久化方式&#xff1a;RDB 持久化和 AOF 持久化。您将了解到它们的工作原理、优缺点以及如何根据需求选择合适的持久化方式。 通过深入学习 Redis 持久化及集群…