C++的魔法世界:类和对象的终章

文章目录

  • 一、再探构造函数
  • 二、类型转换
    • 2.1隐式类型转换
    • 2.2内置类型的类型转化
    • 2.3explicit关键字
    • 2.4多参数构造
  • 三、static成员
  • 四、友元
  • 五、内部类
    • 内部类的特性
  • 六、匿名对象

一、再探构造函数

类和对象(中)里介绍的构造函数,使用的是赋值实现成员变量的初始化。而构造函数还有另一种初始化方法,就是使用初始化列表。

格式:

​ 成员变量以冒号开始,逗号分隔成员,成员变量后面跟上一个括号,其中放置一个初始值或表达式,函数体内实现格外的功能。

class Myqueue
{
public:Myqueue():_pushst(10),_popst(10){}
private:Stack pushst;Stack popst;
}

Myqueue的类编译器自动生成的构造函数调用了Stack的默认构造函数,完成初始化。栈没有默认构造函数,需要使用初始化列表实现自定义类型的默认构造函数。

必须使用初始化列表的类:

​ 没有默认构造的类类型成员变量、被const修饰的成员变量(声明时必须初始化)、被引用的成员变量(声明引用时必须初始化)

  • 自定义类型成员变量,没有默认构造想要调用它的构造需要传参,在函数体内无法实现,那就只能在初始化列表中实现传参。
#include <iostream>
using namespace std;
class Stack
{
public:Stack(int n): _a(new int[n]), _top(0), _capacity(n){}
private:int* _a = new int;int _top = 0;int _capacity = 0;
};
class Myqueue
{
public:Myqueue():pushst(10),popst(10){cout << "Myqueue()" << endl;}
private:Stack pushst;Stack popst;
};

栈没有实现默认构造函数,只实现了构造函数,在Myqueue中需要对两个栈类的成员变量进行初始化,需要调用它的默认构造而栈类没有实现,从而采用初始化列表。

在初始化列表中可以对这两个成员传参调用它们的构造函数从而实现初始化,而若是在函数体内实现初始化,是无法调用这两个栈类成员变量的构造函数。

  • 每个成员变量在初始化列表中只能出现一次,语法理解上初始化列表可以认为是每个成员变量定义初始化的地方
    • 由于某些原因,C++规定必须给每一个成员变量找一个定义的地方,比如被const修饰的变量、引用变量它们在定义的时候必须初始化。若是在函数体内实现,每个成员可以出现多次不能确定那块位置是否为成员变量的初始化。
class A
{
public:A(int n = 4): _n(new int[n]), _a(n), _b(n){}
private:int* _n;int& _a;const int _b;
};

若没有使用初始化列表完成 _a、_b成员变量的初始化,编译时会出现这种报错。

error C2530: “A::_a”: 必须初始化引用 error C2789: “A::_b”: 必须初始化常量限定类型的对象

多次初始化编译器也会提示你,某变量已经初始化了。

  • C++支持在成员变量声明的地方给缺省值,给没有在初始化列表显示初始化的成员变量使用

给缺省值的同时也对成员变量在初始化列表初始化

class B
{
public:B(int n = 4): _n(new int[n]), _a(n), _b(n), _st(4){}
private:int* _n = new int;int& _a;const int _b = 2024;Stack _st = 10;
};

在这里插入图片描述

可以发现,运行结果都是按照初始化列表给定的值完成初始化。在这里插入图片描述

给定缺省值,不在初始化列表初始化,编译器会自动调用缺省值使成员变量在初始化列表完成初始化。

  • 尽量在初始化列表实现成员变量的初,始化,如果咱不写编译器也会将其放在初始化列表初始化。内置类型随机赋值或连续赋值,自定义类型调用它的默认构造

  • 初始化列表按照成员变量声明的顺序进行初始化,所以在初始化列表中最好按照声明顺序进行定义别把顺序打乱了。

二、类型转换

2.1隐式类型转换

造构造函数中不仅可以构造初始化对象,对于单个参数或者第一个参数无缺省值其余参数均有默认值的构造函数还具有类型转换的作用

class Date
{
public:Date(int year):_year(year){}
private:int _year;int _month = 3;int _day = 7;
};
int main()
{Date d1 = 1;return 0;
}

上述代码将一个整形赋值给一个自定义类型,这种逻辑很明显是错误的,但是Date类实现了,单个参数的构造函数,使得整形1在赋值给d1对象时,发生了隐式类型转换,将整形1转换为Date类型,从而进行赋值。

而实际的流程:Date里实现的有通过整形为形参实现的单参数构造函数,尝试使用一个int对象给一个Date对象赋值后,编译器使用int对象调用构造函数生成一个Date类型的临时对象,最后通过临时对象调用拷贝构造函数实现对Date对象的赋值。

  • 构造函数–生成临时对象–调用拷贝构造–完成赋值

对于VS编译器,调用构造函数 + 拷贝构造函数的过程,会被优化为:调用构造函数。这是编译器在效率上的优化提升,不同版本编译器的优化效果和逻辑大不相同,类如DEVC++编译器可能就不会去进行优化。


注意:编译器生成的临时对象具有常性

如下图:前面提过,隐式类型转换实际上通过调用构造函数生成临时对象,而临时对象又去调用拷贝构造完成赋值的过程,这里的临时对象具有常性,下图的**普通d1对象尝试对一个具有常性的对象进行引用,将会导致权限放大,**从而引发编译器报错。

在这里插入图片描述

将d1对象使用const修饰后就不会出现这种问题
在这里插入图片描述

但这又引出了新的问题,临时对象在调用完拷贝构造函数,出了作用域它就会被销毁了,而常性d1对象对临时对象使用了引用,这导致d1对象对一块被销毁引用,是野引用.

2.2内置类型的类型转化

对于内置类型的转换,实际上也存在生成临时对象,然后完成拷贝赋值的过程。

int main()
{//类似的还有int i = 1;double j = i;//ok~没问题隐式类型转换double& j = i//ok~这就有问题,i生成的临时对象具有常性,j想指向它导致权限放大,编译器报错。const double& j = i;//使用const修饰,权限平移。return 0;
}

2.3explicit关键字

当不期望,隐式类型转换的发生,可以在构造函数前添加关键字: ecplicit

它的功能:禁止使用隐式类型装换

在这里插入图片描述

2.4多参数构造

以上介绍的都是单参数的类型转换,而多参数的构造实现后,该如何赋值?

class Date
{
public:Date(int year, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
private:int _year;int _month = 3;int _day = 7;
};
int main()
{Date d1 = 2024,10,1;//奇奇怪怪的赋值方法,从来没有见过,当然编译器也不支持这种写法return 0;
}

多参数的类型装换需要使用 {}花括号 Date d1 = {2024, 10, 1};,C++11才开始支持

int main()
{Date d1 = {2024, 10, 1};return 0;
}

同理若是,多参数的构造函数不期望支持隐式类型转化,使用关键字 explicit修饰即可。

三、static成员

  • 用static修饰的成员变量,称为静态成语变量,静态成员变量一定要在类外面进行初始化
  • 静态成员变量为所有类成员共享,不属于某个具体的对象,不存在类中,存在静态区
class pp
{
private:int _z;int _y;static int _x;
};
int pp::_x = 1;
  • 用static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针
  • 非静态的成员函数,可以访问任意位置的成员变量和成员函数
    • 静态成员函数,只能访问静态成员变量和静态成员函数,它没有this指针
class pp
{
public:pp(int z = 3, int y = 2):_z(z),_y(y){}static int Get_x(){_z++;return _x;}void Fun(){_x++;_y++;}
private:int _z;int _y;static int _x;
};
int pp::_x = 1;

类如上述静态成员函数,它被static修饰后就没有this指针,在 Get_x 函数内调用就回引起编译器报错。 error C2597: 对非静态成员“pp::_z”的非法引用

Fun函数就不会有什么问题,它并不是静态成员函数,函数内部使用this指针,和静态成员变量都是正确的。

  • 静态成员也受 private、public、protected访问限定符的限制

  • 突破类域就可以访问静态成员变量,可以通过 类名::静态成员 或者 对象.静态成员来访问静态成员变量、函数。

    class pp
    {
    public:pp(int n = 1){_z = n;_y = n;}
    private:int _z;int _y;
    public:static int _x;
    };
    int pp::_x = 10;
    int main()
    {pp p;cout << p._x << endl;cout << pp::_x << endl;return 0;
    }
    

若静态成员变量收限定符的限制,无法通过类名或对象类进行访问,此时可以写一个get函数来获取静态成员函数的大小。

  • 静态成员变量不能在声明位置给缺省值初始化,这个缺省值是构造函数初始化列表使用的,而静态成员变量不属于某个对象,不能走构造函数初始化列表。
class A
{
private:static int _count;
}
int A::_count = 1;int main()
{A a1;A a2;A a3;//a1、a2、a3都可以访问cout << A::_count <<endl;cout << a1._count << endl;//没有被private修饰,两种突破类域的方法a1.GetCount();//或者在类中实现获取count的函数。return 0;
}

四、友元

友元,是一种用于突破类访问限定符封装的方式,通过它就可以使外部函数访问类的私有和包含成员。而友元分为:友元函数、友元类。在函数声明或类声明的位置前面加friend,并将其放在一个类中。

  • 友元函数/类可以访问类的私有和保护成员,友元函数仅仅是一种声明,他不是类的成员。
class Date
{friend ostream& operator<<(ostream& out, const Date d);
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
ostream& operator<<(ostream& out, const Date d)
{cout << d._year << "年" << d._month << "月" << d._day << "日" << endl;
}
  • 友元函数/类可以在类定义的任何地方使用,不受访问限定符限制
  • 一个函数可以是多个类的友元
#include <iostream>
using namespace std;class hh;//前置声明//若不做声明,pp中的友元函数声明不认识hh类
class pp
{friend void Fun(const pp& p, const hh& h);
public:pp(int n = 2): _a(n), _b(n){}private:int _a;int _b;
};
class hh
{friend void Fun(const pp& p, const hh& h);
public:hh(int n = 5): _m(n), _n(n){}private:int _m;int _n;
};
void Fun(const pp& p, const hh& h)
{cout << h._m << endl;cout << p._a << endl;
}
  • 友元类的关系是单向的,A类是B类的友元,B类不是A类的友元。

    • 就好比张三和李四是好盆友,李四和王五是好朋友,但这并不代表王五是张三的好朋友,他们两个认不认识还不一定
  • 友元类的关系不能传递,A类是B类的友元,B类是C类的友元,但A类不是C类的友元。

    • 类似友人B找了两个女朋友,分别是友人A、友人C,这时候友人B脚踏两只船。

      而友人B可不希望两个女朋认识,友人B都给女朋友说我只爱你一个人,然而呢~,某一天友人B和友人A出去逛商场的时候恰巧碰见了友人C,诶哟我去!这个场面给位自己想象吧 ~,嘿嘿

  • 友元的存在增加了耦合度,破坏了封装,不宜多用。

五、内部类

如果一个类定义在另一个内的内部,那就将这个类称之为内部类

概念:

内部类是一个独立的类。内部类与定义在全局相比,内部类受到了外部类类域限制和访问限定符的限制,外部类定义的对象中不包含内部类

下列,在类M中定义了一个N类,那这个N类就是内部类

class M
{
public:M(int n = 3):_a(n),_b(n){}
private:int _a;int _b;public:class N{public:void Fun(const M& m){cout << m._a << endl;}private:int _x;};
};
int main()
{M m;cout << sizeof(m) << endl;return 0;
}
  • 内部类天生就是外部类的友元,内部类可以通过外部类的对象参数来访问外部类中所有成员,但外部类不是内部类的友元

  • N类包在M类里面,若需要单独实例化N类,将会收到外部类类域的限制,此时需要使用域作用限定符 ::
    在这里插入图片描述

内部类的特性

  • 内部类定义在外部类的public、private、protected都是可以的
class pp
{
public:class A{private:int _a;};
private:class B{private:int _b;};
protected:class C{private:int _c;};
};
  • 由于内部类是外部类的友元,内部类在访问外部类的不需要加 类名/对象名

  • 内部类并不会占用外部类的空间容量,使用sizeof计算外部类的大小不会与内部类有关

  • 若两个类的关系紧密相连,B类的功能主要提供给A类使用,就可以考虑将B类实现为A类的内部类。若给内部类使用访问限定符修饰(private/protected),那这个内部类就是外部类的专属,其余类无法使用。

六、匿名对象

匿名对象,没有类名的对象。语法:类名()

可以发现,在下列代码中定义了两个对象:一个有名字,一个没有名字。没有名字的对象就称为匿名对象。

#include <iostream>
using namespace std;
class C
{
public:C(int n = 10){cout << "调用构造" << endl;_c = n;}~C(){cout << "调用析构" << endl;}int ret() { return _c; }
private:int _c;
};
int main()
{C c1(66);C(6);return 0;
}

首先介绍匿名对象特别重要的一个特性:匿名对象的声明周期只有当前行,当编译器读到下一行代码时,匿名对象就会被自动销毁了。

在这里插入图片描述

  • 没有对象名的匿名对象也可以调用成员函数

在这里插入图片描述

  • 匿名对象的引用

    • 匿名对象具有常性,被引用需要将const修饰,这样做它的生命周期被延长了,不会即用即销毁,在程序运行结束后才会销毁。const A& r = A(); 但它被引用后而匿名对象的空间被销毁,导致野引用,这在语法上是不允许的
    • 反之,没有使用const进行修饰,在编译时会引起编译器的报错

在这里插入图片描述

匿名对象作函数缺省值:

class A
{
public:A(int n = 2):_a(n){}~A(){cout << "~A()" << endl;}
private:int _a;
};
void fun(A aa = A(1))//函数缺省值,给匿名对象
{}
int main()
{A();//这是一个匿名对象。没有参数也必须加上括号A(10);//这是一个匿名对象。return 0;
}

在这里插入图片描述

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

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

相关文章

出现接地故障电流现象,安科瑞ASJ剩余电流继电器可以避免吗?

什么是ASJ剩余电流继电器 剩余电流继电器是检测剩余电流&#xff0c;并将剩余电流值与基准值相比较的电器。当剩余电流值超过基准值时&#xff0c;它会发出一个机械开闭信号&#xff0c;使机械开关电器脱扣或声光报警装置发出报警。这种继电器通常基于漏电保护原理工作&#x…

【QAMISRA】解决导入commands.json时报错问题

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 解决导入commands.json时报错“Could not obtain system-wide includes and defines”的问题。 2、 问题场景 客户导入commands.json时报错“Could not obtain system-wide includes and defines”。 3、软硬件环境…

【保姆级教程】DolphinScheduler本地部署与远程访问详细步骤解析

文章目录 前言1. 安装部署DolphinScheduler1.1 启动服务 2. 登录DolphinScheduler界面3. 安装内网穿透工具4. 配置Dolphin Scheduler公网地址5. 固定DolphinScheduler公网地址 前言 本篇教程和大家分享一下DolphinScheduler的安装部署及如何实现公网远程访问&#xff0c;结合内…

海思hi3536c配置内核支持USB摄像头

linux内核版本&#xff1a;linux-3.18.20 配置步骤 进入Device Drivers 选择Multimedia support&#xff0c;并进入 选择Media USB Adapters&#xff0c;并进入 如下图&#xff0c;选择这几项&#xff1a; 保存退出&#xff0c;重新编译内核下载 内核更新后&#xff0c…

家里有宠物想去异味,希喂、米家、范罗士宠物空气净化器哪款去异味好?

宠物的便臭和体臭&#xff0c;其臭味浓度和持续性&#xff0c;相比于正常家居的其他臭味&#xff0c;祛除难度更大&#xff0c;建议就是选使用真正能高效除臭、分解异味分子的化学分解法除臭的宠物空气净化器。比如&#xff1a;光触媒分解除臭的。 不踩坑前置推荐 我从2020年…

docker-compose 部属netcore

一、准备镜像 编写&#xff1a;dockercompose.yml version: "3.4"services: saas.demo.api: image: harbor.net.com/demos/saas.demo.api:latestcontainer_name: saas.demo.apienvironment:- ASPNETCORE_ENVIRONMENTProductionports: - "5001:80" 部属&am…

CTFHUB技能树之HTTP协议——响应包源代码

开启靶场&#xff0c;打开链接&#xff1a; 是个贪吃蛇小游戏&#xff0c;看不出来有什么特别的地方 用burp抓包看看情况&#xff1a; 嗯&#xff1f;点击“开始”没有抓取到报文&#xff0c;先看看网页源代码是什么情况 居然直接给出flag了&#xff0c;不知道这题的意义何在 …

UE4 材质学习笔记06(布料着色器/体积冰着色器)

一.布料着色器 要编写一个着色器首先是看一些参考图片&#xff0c;我们需要找出一些布料特有的特征&#xff0c;下面是一个棉织物&#xff0c;可以看到布料边缘的纤维可以捕捉光线使得边缘看起来更亮 下面是缎子和丝绸的图片&#xff0c;与棉织物有几乎相反的效果&#xff0c;…

Docker 容器 数据卷 使用

目录 常用 命令 什么是数据卷以及特点 如何挂载数据卷 数据卷容器 数据覆盖问题 修改已经建立的数据卷关系 博主wx&#xff1a;yuanlai45_csdn 博主qq&#xff1a;2777137742 想要 深入学习 5GC IMS 等通信知识(加入 51学通信)&#xff0c;或者想要 cpp 方向修改简历&…

Linux——用户/用户组

创建用户组groupadd groupadd 用户组 删除用户组groupdel groupdel 用户组 创建用户useradd useradd 用户名 - g 用户组 useradd 用户名 -d HOME路径 删除用户userdel userdel 用户 userdel -r 用户 &#xff08;删除用户的 HOME 目录&#xff0c;不使用 -r &#xff0…

java项目之纺织品企业财务管理系统源码(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的纺织品企业财务管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 基于spring boot…

西门子网络程序传输,无需开通网络驱动器直接接入底层,支持各类数控 如发那科、三菱 、新代、海德汉、广数、精雕、马扎克等等

有关西门子的程序传输问题&#xff0c;大家一般是通过文件共享、ftp、网络驱动器等方式&#xff0c;其中828D还需要授权开通网络启动器 下面介绍一种方式直接进入西门子Linux底层系统实现和NCK的文件交互功能 软件截图如下 功能表如下 机床程序上载至电脑 电脑程序下传…

什么是 Spring 引导的执行器?

大家好&#xff0c;我是锋哥。今天分享关于【什么是 Spring 引导的执行器&#xff1f;】面试题&#xff1f;希望对大家有帮助&#xff1b; 什么是 Spring 引导的执行器&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Spring Boot 的执行器&#xff08;…

Unity 从零开始搭建一套简单易用的UGUI小框架 基础分析篇

一套UGUI的小框架用一篇文章显然是不够的&#xff0c;因为会很长很长大约有上万字&#xff0c;想必读者也没有那个耐心一点点读完&#xff08;主要是我也懒&#xff09;&#xff0c;所以我就将其分为三个部分 基础分析篇 功能撰写与优化篇 扩展与总结篇 我将其都放在了同一个专…

抓取指定网站上的所有图片的Python脚本

引言 在当今信息爆炸的时代&#xff0c;互联网上的数据量呈现出指数级的增长。对于开发者、数据分析师以及研究人员而言&#xff0c;从网页中提取有价值的信息是一项至关重要的技能。其中&#xff0c;抓取网站上的图片资源不仅能够丰富我们的数据集&#xff0c;还能为各种应用…

Linux操作系统分析实验-文件操作,实验三

一、实验目的 1、 理解Linux中虚拟文件系统的内容 2、 学习编写内核模块的方法 3、 在虚拟文件系统/proc中实现文件操作算法 二、实验内容 编写一个内核模块&#xff0c;在/proc文件系统中增加一个目录hello&#xff0c;并在这个目录中增加一个文件world&#xff0c;文件的…

推荐给前端同学的自动化测试库

对于前端开发而言&#xff0c;自动化测试不仅能够提高开发效率&#xff0c;还能确保应用的稳定性和可靠性。而Python提供了多种适用于前端自动化测试的库。这些库能够帮助前端开发者轻松实现UI测试、API测试和性能测试等多种需求。本文将介绍几个Python中常用的前端自动化测试库…

YOLO11改进-模块-引入Histogram Transformer Block(HTB)解决恶劣天气(雨雾雪)

本篇文章将介绍一个新的改进机制——HTB&#xff0c;并阐述如何将其应用于YOLOv11中&#xff0c;显著提升模型性能。在现代计算机视觉任务中&#xff0c;尤其是在目标检测领域&#xff0c;YOLO系列模型因其快速和准确的检测性能而备受关注。随着YOLOv11的提出&#xff0c;我们迎…

CTF(二)

导言&#xff1a; 本文主要讲述在CTF竞赛中&#xff0c;web类反序列化题目unseping。。 靶场链接&#xff1a;攻防世界 (xctf.org.cn) 反序列化漏洞&#xff1a;反序列化漏洞&#xff08;二&#xff09;_fst反序列化 rocksdb 字段值错误-CSDN博客 打开后可以看到&#xff1…

涂鸦智能落地 Koupleless 合并部署,实现云服务降本增效

文&#xff5c;八幡、朵拉 杭州涂鸦智能技术专家 主要研究微服务与可观测、消息引擎、任务调度、数据层中间件等领域。 本文 5389 字 阅读 15 分钟 当前涂鸦通过 Koupleless 的静态合并部署能力&#xff0c;很好地解决了资源浪费问题。 为了进一步提升研发效率&#xff0c;涂鸦…