C++之类和对象(3)

目录

1. 再谈构造函数

1.1 构造函数体赋值

 1.2 初始化列表

1.3 explicit 

 2. static成员

2.1 概念

 3. 友元

3.1 友元函数

3.2 友元类

4. 内部类

 5. 匿名对象

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


1. 再谈构造函数

1.1 构造函数体赋值

class Date
{
public:Date(int year=2024, int month=3, int day=16){_year = year;_month = month;_day = day;}void pri() {cout << _year <<" "<< _month <<" "<< _day << endl;}
private:int _year;int _month;int _day;
};
int main() {Date a;Date b(111);a.pri();b.pri();return 0;
}

虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。

 

 1.2 初始化列表

class Date
{
public:Date(int year, int month, int day)//初始化列表: _year(year), _month(month), _day(day){}void pri() {cout << _year << " " << _month << " " << _day << endl;}
private:int _year;int _month;int _day;
};
int main() {Date a(1,2,3);a.pri();return 0;
}


初始化列表是每个成员变量定义初始化的位置

 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)

const成员变量无法在函数体初始化,得在初始化列表初始化

int& ref;引用也得在初始化列表,因为引用定义必须初始化

自定义类型成员(且该类没有默认构造函数时)也在初始化列表



 

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

最后输出 1   -858993460

因为先初始化a2因为a2先在类中声明而且用的是a1的值初始化而a1是随机值

a1最后初始化为a,是1

能用初始化列表就用初始化列表

成员变量的顺序与初始化列表的顺序最好一致,因为成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后 次序无关

1.3 explicit 

单参数类型: 

class A {
public:A(int a=0) :_aa(a){cout <<_aa << endl;}private:int _aa;
};
class B {
private:int _a;int* p = nullptr;int* pp = (int*)malloc(4);
};
int main() {B bb;A c1(1);A c2 = 2;//单参数构造函数支持隐式类型转换//所以是2构造一个临时对象然后拷贝构造const A& c3 = 4;return 0;
}

缺省值可以是其他的变量不局限于常量
如果不想存在隐式类型转换的话可以加explicit在构造函数前面:

c2和c3就出错了

 多参数类型:

class A {
public:A(int a=0,int b=1) :_aa(a),_bb(b){cout <<_aa <<" ";cout << _bb << endl;}private:int _aa;int _bb;
};
class B {
private:int _a;int* p = nullptr;int* pp = (int*)malloc(4);
};
int main() {B bb;A c1(1,2);A c2 = {3,4};const A& c3 = {5,6};return 0;
}
多参数支持花括号产生隐式类型转换

同样加了explicit就不行了:

 用explicit修饰构造函数,将会禁止构造函数的隐式转换

 2. static成员

2.1 概念

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用 static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化

静态成员函数没有this指针所以他只是为了访问静态成员变量他访问不了非静态成员变量因为没this

class A
{
public:A() {++n;}A(const A& aa) {++n;}
//private:static int n;//声明//属于整个类,但本质还是静态全局变量
};
int A::n = 0;//在类外定义
void func() {n += 1;//那么这里就访问不了n了
}
int main() {A a;A b;cout << n << endl;//这里也是访问不了nreturn 0;}

 

class 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(){cout << A::GetACount() << endl;A a1, a2;A a3(a1);cout << A::GetACount() << endl;}int main() {TestA();return 0;}

最终结果0 3

当创建对象 a1 时会调用默认构造函数 A()

输出 _scount 的初始值,初始值为 0。

创建对象 a1,_scount 自增为 1。

创建对象 a2,_scount 再次自增为 2。

通过复制构造函数创建对象 a3,_scount 再次自增为 3。

输出 _scount 的最终值,值为 3。

注意:

1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区

2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明

3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问

4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员

5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制

 3. 友元

友元提供了一种突破封装的方式,有时提供了便利。但是友元破坏了封装,所以友元不宜多用-慎用

3.1 友元函数

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。

class Date
{//友元函数friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);public:Date(int year = 1900, int month = 1, int day = 1):_year(year),_month(month),_day(day){}
private:int _year;int _month;int _day;
};ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "/" << d._month << "/" << d._day << endl;return out;
}
istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}
int main()
{Date d1;cin >> d1;cout << d1;return 0;
}

友元函数是普通的全局函数:

#include <iostream>using namespace std;class A
{private:int A;public:print(){};//声明全局函数 person 是 类A 的友元函数friend void person (int &x);
}void person(int &x)
{//使用了类A的成员变量agecout << "age=" << p.age << endl;}int main ()
{A p(22);person(p);return 0;
}

友元函数是其他类的成员函数:

#include <iostream>using namespace std;class A
{private:int A;public:print(){};//声明类B的成员函数 person 是 类A 的友元函数friend  void B::person (int &x);
}class B
{private:int B;public:person(int &x);}B::person(int &x)
{//因为类B的成员函数person是类A的友元函数,所以看可以使用类A的成员变量agecout << "age=" << p.age << endl;}int main ()
{A p(22);B q;q.person(p);return 0;}

注意:

  • 友元函数可访问类的私有和保护成员,但不是类的成员函数
  • 友元函数不能用const修饰
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  • 一个函数可以是多个类的友元函数
  • 友元函数的调用与普通函数的调用原理相同

3.2 友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。

友元关系是单向的,不具有交换性。
比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接
访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
友元关系不能传递:如果C是B的友元, B是A的友元,则不能说明C时A的友元。
友元关系不能继承(后续解析)

//时间类
class 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;
};

优缺点总结 :

点:友元函数不是类的成员但是却具有成员的权限,可以访问类中受保护的成员,这破坏了类的封装特性和权限管控;

优点:可以实现类之间的数据共享;比如上面互为友元类,则可以互相访问对方受保护的成员;

总结:友元函数是一种破坏封装特性的机制,可以让程序员写代码更灵活,但是不能滥用

4. 内部类

概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。

注意:内部类就是外部类的友元类,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。

特性:

  • 1. 内部类可以定义在外部类的public、protected、private都是可以的。
  • 2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
  • 3. sizeof(外部类)=外部类,和内部类没有任何关系。
    // 1、B类受A类域和访问限定符的限制,其实他们是两个独立的类
    // 2、内部类默认就是外部类的友元类
    class A
    {//外部类不能访问内部类
    public:class B // B天生就是A的友元{public:void print(const A& a){cout << k << endl;   //可以直接访问A的静态成员变量cout << a.h << endl; //也可以访问A的成员变量}};
    private:static int k;int h;
    };
    int A::k = 1;int main()
    {A::B b;b.print(A());cout << sizeof(A) << endl;  //8   外部类的大小与内部类无关  a与b相互独立的只不过b受a类的局域限制return 0;
    }
    类本身不占用空间

C++不太喜欢使用内部类,所以了解即可 

 5. 匿名对象

匿名对象的特点就是不用取名字,生命周期只存在定义的这一行。

class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};int main()
{A aa1;//A aa1();// 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,A();// 但是他的生命周期只有这一行,紧接着它的下一步就会自动调用析构函数A aa2(2);return 0;
}

 应用场景: 当我们做C++的OJ题时会发现都是将其封装在一个Solution类中的,假设我们需要调用这个类中的某一个函数,是需要先创建一个Solution的对象,然后通过这个对象进行调用,这样的话有点麻烦,我们可以直接使用匿名对象来调用这个类中的成员函数。

class Solution {
public:int Sum_Solution(int n) {//...return n;}
};
int main()
{// 1.基本方法Solution sl;sl.Sum_Solution(10);// 2.匿名对象Solution().Sum_Solution(10);return 0;
}

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

在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还是非常有用的

class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}A(const A& aa):_a(aa._a){cout << "A(const A& aa)" << endl;}A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a = aa._a;}return *this;}~A(){cout << "~A()" << endl;}
private:int _a;
};
void f1(A aa)
{}
A f2()
{A aa;return aa;
}
int main()
{// 传值传参A aa1;f1(aa1);cout << endl;// 传值返回f2();cout << endl;// 隐式类型,连续构造+拷贝构造->优化为直接构造f1(1);// 一个表达式中,连续构造+拷贝构造->优化为一个构造f1(A(2));cout << endl;// 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造A aa2 = f2();cout << endl;// 一个表达式中,连续拷贝构造+赋值重载->无法优化aa1 = f2();cout << endl;return 0;
}


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

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

相关文章

Helm Chart部署最简SpringBoot到K8S(AWS EKS版)

目标 这里假设&#xff0c;我们已经基本会使用k8s的kubectl命令进行部署了&#xff0c;也已经会自己打docker镜像推送到AWS ECR上面去了。而且&#xff0c;已经在云上准备好了AWS ECR镜像库和AWS EKS的k8s集群了。 这个前提上面&#xff0c;我们今天使用Helm Chart项目准备k8s…

数据结构试卷第九套

1.时间复杂度 2.树&#xff0c;森林&#xff0c;二叉树的转换 2.1树转二叉树 给所有的兄弟节点之间加一条连线&#xff1b;去线&#xff0c;只保留当前根节点与第一个叶子节点的连线&#xff0c;删除它与其他节点之间的连线&#xff1b;然后根据左孩子右兄弟进行调整&#xf…

个人网站制作 Part 11 添加用户权限管理 | Web开发项目

文章目录 &#x1f469;‍&#x1f4bb; 基础Web开发练手项目系列&#xff1a;个人网站制作&#x1f680; 添加用户权限管理&#x1f528;使用Passport.js&#x1f527;步骤 1: 修改Passport本地策略 &#x1f528;修改用户模型&#x1f527;步骤 2: 修改用户模型 &#x1f528…

医学数据分析中缺失值的处理方法

医学数据分析中缺失值的处理方法 &#xff08;为了更好的展示&#xff0c;在和鲸社区使用代码进行展示&#xff09; 医学数据分析中&#xff0c;缺失值是不可避免的问题。缺失值的存在会影响数据的完整性和准确性&#xff0c;进而影响分析结果的可靠性。因此&#xff0c;在进…

中创ET4410台式电桥固件升级工具(修复了列表扫描的BUG)

中创ET4410台式LCR数字电桥固件升级工具和最新版固件&#xff08;修复了列表扫描的BUG&#xff09; 中创ET4410 台式LCR数字电桥 简单开箱测评&#xff1a;https://blog.zeruns.tech/archives/763.html 之前买的中创ET4410台式LCR数字电桥固件有BUG&#xff08;胜利的VC4090C…

FREERTOS中断配置和临界段

本文基础内容参考的是正点原子的FREERTOS课程。 这是基于HAL库的 正点原子手把手教你学FreeRTOS实时系统 这是基于标准库的 正点原子FreeRTOS手把手教学-基于STM32 回顾STM32的中断 什么是中断&#xff1f; 中断优先级分组设置 Freertos中断分组 Freertos就是用的最后一种&…

redis学习-redis介绍

目录 1.redis介绍 2.redis常用命令&#xff08;可以在官网的命令中查看redis的所有命令&#xff09; 2.1终端命令 2.2 redis通用命令 2.3五大基本类型的命令以及特殊情况分析 &#xff08;导航&#xff09; 3.事务 4. redis实现消息订阅 5. redis的两种持久化策略 …

KubeSphere集群安装-nfs分布式文件共享-对接Harbor-对接阿里云镜像仓库-遇到踩坑记录

KubeSphere安装和使用集群版 官网:https://www.kubesphere.io/zh/ 使用 KubeKey 内置 HAproxy 创建高可用集群:https://www.kubesphere.io/zh/docs/v3.3/installing-on-linux/high-availability-configurations/internal-ha-configuration/ 特别注意 安装前注意必须把当前使…

AG32 MCU以太网应用实例demo

一. 前言 AGM32系列32位微控制器旨在为MCU用户提供新的自由度和丰富的兼容外设&#xff0c;以及兼容的引脚和功能。AG32F407系列产品具有卓越的品质&#xff0c;稳定性和卓越的价格价值。 AG32产品线支持其所有接口外设尽可能接近主流兼容性&#xff0c;并提供丰富的参考设计…

排序算法:快速排序(非递归)

文章目录 一、先建立一个栈二、代码编写 !](https://img-blog.csdnimg.cn/direct/870dd101173d4522862e4459b32237a3.png) 先赞后看&#xff0c;养成习惯&#xff01;&#xff01;&#xff01;^ _ ^<3 ❤️ ❤️ ❤️ 码字不易&#xff0c;大家的支持就是我坚持下去的动力…

旧华硕电脑开机非常慢 电脑开机黑屏很久才显示品牌logo导致整体开机速度非常的慢怎么办

前提条件 电池需要20&#xff05;&#xff08;就是电池没有报废&#xff09;且电脑接好电源&#xff0c;千万别断电&#xff0c;电脑会变成砖头的 解决办法 更新bios即可解决&#xff0c;去对应品牌官网下载最新的bios版本就行了 网上都是一些更新驱动啊

传统机器学习 基于TF_IDF的文本聚类实现

简介 使用sklearn基于TF_IDF算法&#xff0c;实现把文本变成向量。再使用sklearn的kmeans聚类算法进行文本聚类。 个人观点&#xff1a;这是比较古老的技术了&#xff0c;文本转向量的效果不如如今的 text2vec 文本转向量好。 而且sklearn 不支持GPU加速&#xff0c;处理大量…

小狐狸ChatGPT智能聊天系统源码v2.7.6全开源Vue前后端+后端PHP

测试环境包括Linux系统的CentOS 7.6&#xff0c;宝塔面板&#xff0c;PHP 7.4和MySQL 5.6。网站的根目录是public&#xff0c; 使用thinkPHP进行伪静态处理&#xff0c;并已开启SSL证书。 该系统具有多种功能&#xff0c;包括文章改写、广告营销文案创作、编程助手、办公达人…

备战蓝桥杯Day26 - 二叉搜索树查询和删除操作

一、查询 递归查询 寻找的值比根节点大&#xff0c;遍历右子树&#xff1b; 寻找的值比根节点小&#xff0c;遍历左子树。 def qurey(self, node, val):if not node: # 没有节点&#xff0c;返回空return Noneif node.data < val:return self.qurey(node.rchild, val)el…

FPGA高端项目:FPGA基于GS2971+GS2972架构的SDI视频收发+HLS多路视频融合叠加,提供1套工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐本博已有的 SDI 编解码方案本方案的SDI接收发送本方案的SDI接收图像缩放应用本方案的SDI接收纯verilog图像缩放纯verilog多路视频拼接应用本方案的SDI接收HLS图像缩放HLS多路视频拼接应用本方案的SDI接收OSD多路视频融合叠加应用本方案的S…

C++第六弹---类与对象(三)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、类的6个默认成员函数 2、构造函数 2.1、概念 2.2、特性 3、析构函数 3.1、概念 3.2、特性 3.3、调用顺序 总结 1、类的6个默认成员函数…

QT UI窗口常见操作

MainWidget::MainWidget(QWidget *parent): QWidget(parent), ui(new Ui::MainWidget) {ui->setupUi(this);// 设置主窗口背景颜色QPalette plt;plt.setColor(QPalette::Window,QColor(180,220,130));this->setPalette(plt);// 禁止窗口最大化按钮setWindowFlags(windowF…

Spring Boot Starter: 快速简明地创建Spring应用

Spring Boot Starter是Spring Boot的核心功能之一&#xff0c;它帮助开发人员快速简明地创建、配置和运行Spring应用。在本文中&#xff0c;我们将详细介绍Spring Boot Starter以及如何使用它创建一个Spring Boot应用。 文章目录 什么是Spring Boot Starter?为何使用Spring B…

DevOps 环境预测测试中的机器学习

在当今快节奏的技术世界中&#xff0c;DevOps 已成为软件开发不可或缺的一部分。它强调协作、自动化、持续集成&#xff08;CI&#xff09;和持续交付&#xff08;CD&#xff09;&#xff0c;以提高软件部署的速度和质量。预测测试是这一领域的关键组成部分&#xff0c;其中机器…

Transformer的前世今生 day02(神经网络语言模型

神经网络语言模型 使用神经网络的方法&#xff0c;去完成语言模型的两个问题&#xff0c;下图为两层感知机的神经网络语言模型&#xff1a; 以下为预备概念 感知机 线性模型可以用下图来表示&#xff1a;输入经过线性层得到输出 线性层 / 全连接层 / 稠密层&#xff1a;假…