C++类与对象(补)

 

感谢大佬的光临各位,希望和大家一起进步,望得到你的三连,互三支持,一起进步

个人主页:LaNzikinh-CSDN博客

文章目录

  • 前言
  • 一.默认成员函数
  • 二.static
  • 三.友元
  • 四.匿名对象
  • 总结

前言

类的默认成员函数,默认成员函数就是用户没有显式实现,编译器会自动生成的成员函数称为默认成员函数。一个类,我 们不写的情况下编译器会默认生成以下6个默认成员函数。


一.默认成员函数

1.1构造函数

因为前面对构造函数进行过了讲解,这里只做补充

注意:构造函数就是替代了以前C语言中的初始化,对象实例化时系统会自动调用对应的构造函数,构造函数是可以重载的

默认构造函数概念:1.无参构造函数,2.全缺省的,3.不写构造时编译器默认生成的。这三个函数有且只能有一个存在,总结:不传实参数就可以调用的构造叫做默认构造

对于编译器默认生成的构造,对内置类型成员变量初始化没有要求,但对于自定义类型成员变量,会调用这个成员变量的默认构造函数,如果这个成员变量没有构造函数就会报错,这时候就用初始化成员列表。

那么什么是内置类型和自定类型呢?C++把类型分为两类,内置类型和自定义类型,内置类型就是int,char,float,指针这种,自定义类型就是类,结构体,class,struct

这句话不是很好理解,我们来举个例子,方便理解一下

例子:

这段代码运行来是没有任何问题的,但是如果修改就会有问题

#include<iostream>
using namespace std;
class Date
{
public:Date(){_year = 1;_month = 1;_day = 1;}Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;Date d2(2025, 1, 1); Date d3();return 0;
}

我们如果把第二个构造函数给注释调的话,那么程序就会出错,因为我第二个对象创造的时候没有构造函数给他调用,所以就会报错

同理,如果我把第一个注释掉的话也会报错,因为第一个和第三个没有对应的构造函数

那如果我把两个构造函数全部注释掉可以吗?系统不是有一个默认的吗?系统会帮我们生成一个没有参数的无参构造函数,那么如果想要程序运行成功的话,就要把第二个对象也注释

那么默认的构造函数到底什么时候用呢?或者是说我什么时候要写构造函数什么时候不写呢?我们之前学过一个用栈来构造队列,如果把它用C++来写的话

class MyQueue
{
public:private:Stack pushst;Stack popst;
};

编译器默认生成MyQueue的构造函数调用了Stack的构造,完成了两个成员的初始化

如果我的成员对象是有构造函数的时候,他会先调用成员对象的构造函数,这种情况下如果帮我初始化了的话,我就不需要初始化了,我就不需要调用了,总结:大多数情况下,构造函数都需要我们自己来实现,少数情况下不需要

1.2析构函数

析构函数不想做太多的补充,跟之前一样,析构函数对应着就是C语言中的销毁,注意:自定义类型的成员也会调用它的析构,也就是说自定义类型成员无论什么情况都会自动调用他的析构函数。

1.3拷贝构造函数

拷贝构造函数是类与对象中的一个难点,拷贝构造函数是构造函数的一个重载,他有一个特别的地方,就是第一个参数必须是自身类型的引用,不然会引发无穷递归调用的报错

C++语法规定,传值传参会掉用拷贝构造函数。

我们之前讲过引用的本质是常量指针,所以说,如果不是引用的话,他就会无限递归

若未显示定义拷贝构造函数,编译器会自动生成一个拷贝构造函数,自动生成的拷贝构造对内置类型成员变量会完成浅拷贝,对自定义类型的成员变量会调用它的拷贝构造函数

这个地方和前面的有所差距,比如说我们再用那个站来实现队列的例子,这个地方就不能用系统自己的拷贝构造了

int main()
{Stack st1;st1.Push(1);st1.Push(2);// Stack不显示实现拷贝构造,用自动生成的拷贝构造完成浅拷贝// 会导致st1和st2里面的_a指针指向同⼀块资源,析构时会析构两次,程序崩溃Stack st2 = st1;MyQueue mq1;// MyQueue自动生成的拷贝构造,会自动调用Stack拷贝构造完成pushst/popst// 的拷贝,只要Stack拷贝构造自己实现了深拷贝,他就没问题MyQueue mq2 = mq1;return 0;
}

这里我们在前面的类与对象中讲了浅拷贝和深拷贝的问题,在这个地方就不多做解释了。

Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}

1.4赋值运算符重载

赋值运算符重载就是把一个对象复制给另一个对象。拷贝能分两种,一种是赋值重载拷贝,一种是拷贝构造,那到底怎么区分呢?赋值重载就是两个都存在的对象进行赋值,而拷贝构造是把初始化的对象去赋给没有初始化的对象。

int main()
{Date d1(2024, 7, 5);Date d2(2024, 7, 6);//赋值重载拷贝operator==(d1, d2);d1 == d2;//拷贝构造Date d3(d2);Date d4 = d2;return 0;
}

C++规定类类型对象使用运算符时,必须转换成调用对应运算符重载,若没有对应的运算符重载,则会编译报错。运算符重载是具有特名字的函数,他的名字是由operator和后⾯要定义的运算符共同构成。和其他函数一样,它也具有其返回类型和参数列表以及函数体。

bool operator==(const Date& d1, const Date& d2)
{
return d1._year == d2._year
&& d1._month == d2._month
&& d1._day == d2._day;
}

.*   ::    sizeof   ?:   . 注意以上5个运算符不能重载

重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,无法很好的区分。
C++规定,后置++重载时,增加一个int形参,跟前置++构成函数重载,方便区分。

重载<<和>>时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第一个个形参位置,第一个形参位置是左侧运算对象,调用时就变成了 对象<<cout,不符合使用习惯和可读性。重载为全局函数把ostream/istream放到第一个形参位置就可以了,第二个形参位置当类类型对象。

这个地方不做过多的讲解,到时候会专门来出一个日期类的实现,就专门运用这个赋值运算符重载来实现的。

1.5取地址运算符重载

将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后面。 • const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。const修饰Date类的Print成员函数,Print隐含的this指针由 Date* const this 变为 const Date* const this

class Date
{public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// void Print(const Date* const this) constvoid Print() const{cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{//这里非const也可以调用const成员函数这是⼀种权限的缩⼩Date d1(2024, 7, 5);d1.Print();const Date d2(2024, 8, 5);d2.Print();return 0;
}

1.6*取地址运算符重载

取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,⼀般这两个函数编译器自动生成的就可以够我们用了,不需要去显示实现。除非⼀些很特殊的场景,逼如我们不想让别人取到当 前类对象的地址,就可以自己实现⼀份,胡乱返回⼀个地址。

class Date
{
public:Date* operator&(){return this;// return nullptr;}const Date * operator&()const{return this;// return nullptr;}
private:int _year;int _month; int _day; 
};

二.static

用static修饰的成员变量,称之为静态成员变量,静态成员变量⼀定要在类外进行初始化,因为不存在对象中,存放在静态区用static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针,静态成员也是类的成员,受public、protected、private 访问限定符的限制。

class A
{static int GetACount()
{return _scount;
}
private:// 类⾥⾯声明static int _scount;
};
// 类外⾯初始化
int A::_scount = 0;
int main()
{return 0;}

例子:求1+2+3+...+n_牛客题霸_牛客网 (nowcoder.com)

描述

求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

数据范围: 0<𝑛≤2000<n≤200
进阶: 空间复杂度 𝑂(1)O(1) ,时间复杂度 𝑂(𝑛)O(n)

这个题目就和静态成员变量有很大的关系了,如果不用静态成员变量的话,将很复杂,因为这个题目不能用循环这些语句来做,我们可以用C + +的构造函数的特性先创造一个类,专门用来求和,然后在类中初始化两个静态成员变量,一个初始化1,一个初始化0为什么要用到静态成员变量呢?因为它是一个累加的过程,我只有用到静态成员变量才不会出了,函数就销毁,就直接可以一直保存下来。在利用数组反复的调用构造函数完成题目,这里用了变长数组也可以用new来动态开辟。

class Sum
{
public:
Sum()
{
_ret += _i;
++_i;
}
static int GetRet()
{
return _ret;
}
private:
static int _i;
static int _ret;
};
int Sum::_i = 1;
int Sum::_ret = 0;
class Solution {
public:
int Sum_Solution(int n) {
// 变⻓数组
Sum arr[n];
return Sum::GetRet();
}
};

有static成员变量就肯定有static成员函数

1.静态成员函数中可以访问其他的静态成员,但是不能访问非静态的,因为没有this指针。2.非静态的成员函数,可以访问任意的静态成员变量和静态成员函数。 3.突破类域就可以访问静态成员,可以通过类名::静态成员 或者 对象.静态成员来访问静态成员变量和静态成员函数。

#include<iostream>
using namespace std;
class A
{
public:static int GetACount(){return _scount;}
private:static int _scount;
};
int A::_scount = 0;
int main()
{A a1;cout << A::GetACount() << endl;cout << a1.GetACount() << endl;return 0;
}

三.友元

如果我们需要访问内里的私有成员,该如何访问呢?友元提供了⼀种突破类访问限定符封装的方式,友元分为:友元函数和友元类,在函数声明或者类 声明的前面加friend,并且把友元声明放到⼀个类的里面。

class A
{// 友元声明friend void func(const A& aa);
private:int _a1 = 1;
};void func(const A& aa)
{cout << aa._a1 << endl;
}
int main()
{A aa;func(aa);return 0;
}

除了友元函数,还有友元类,友元类中的成员函数都可以是另⼀个类的友元函数,都可以访问另⼀个类中的私有和保护成员。友元类的关系是单向的,不具有交换性,比如A类是B类的友元,但是B类不是A类的友元。友元类关系不能传递,如果A是B的友元, B是C的友元,但是A不是B的友元。

class A
{// 友元声明friend class B;
private:int _a1 = 1;
};
class B
{
public:void func1(const A& aa){cout << aa._a1 << endl;cout << _b1 << endl;}
private:int _b1 = 3;
};
int main()
{A aa;B bb;bb.func1(aa);return 0;
}

内部类

如果⼀个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,跟定义在 全局相比,他只是受外部类类域限制和访问限定符限制,内部类默认是外部类的友元类

class A
{
private:static int _k;int _h = 1;
public:class B // B默认就是A的友元{public:void foo(const A & a){cout << _k << endl; cout << a._h << endl; }};
};
int A::_k = 1;

A是B的外步类,B默认就是A的友元,B是A的友元,B可以访问到A的私有,A不可以访问B的

四.匿名对象

匿名对象生命周期只在当前一行,一般临时定义一个对象当前用一下即可,就可以定义匿名对象,可以说明明对象就是一个一次性产品,比如有些时候我只需要用一次,就没有必要再创造一个类出来

class A
{private:int a = 2;int b = 1;
public:void print(){cout << print << endl;}
};
int main()
{A().print();return 0;
}

总结

这次是对之前的类与对象的一个补充,完善了一下类与对象的实现。

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

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

相关文章

Mongodb数据库(上)

介绍 是一个基于磁盘存储的开源的、文档类型(数据存储格式)的非关系型数据库。 其数据首先是存放到内存中,当内存不够时,它还可以存放到磁盘里面去 优点 基本概念 数据库 mongodb中的数据库默认是’test‘(就是一进去就是直接使用用的test数据库),如果想要使用其他…

【LabVIEW作业篇 - 2】:分数判断、按钮控制while循环暂停、单击按钮获取book文本

文章目录 分数判断按钮控制while循环暂停按钮控制单个while循环暂停 按钮控制多个while循环暂停单击按钮获取book文本 分数判断 限定整型数值输入控件值得输入范围&#xff0c;范围在0-100之间&#xff0c;判断整型数值输入控件的输入值。 输入范围在0-59之间&#xff0c;显示…

【Python进阶】正则表达式、pymysql模块

目录 一、正则表达式的概述 1、基本介绍 2、快速使用re模块 二、正则的常见规则 1、匹配单个字符 2、原始字符串 3、匹配多个字符 4、匹配开头和结尾 5、匹配分组 三、Python与MySQL交互 1、pymysql模块的安装 2、pymysql的操作步骤 3、connection对象 4、cursor…

基于ANSIBLE中的YAML非标记语言Role角色扮演

YAML-YAML Ain’t Markup Language-非标记语言 语法 列表 fruits:​ - Apple​ - Orange​ - Strawberry​ - Mango 字典 martin:​ name : Martin D’vloper​ job : Developer​ skill : Elite 示例1 需求 通过YAML编写一个简单的剧本&#xff0c;完成web的部署&#xff0c…

【Mongodb-04】Mongodb聚合管道操作基本功能

Mongodb系列整体栏目 内容链接地址【一】Mongodb亿级数据性能测试和压测https://zhenghuisheng.blog.csdn.net/article/details/139505973【二】springboot整合Mongodb(详解)https://zhenghuisheng.blog.csdn.net/article/details/139704356【三】亿级数据从mysql迁移到mongodb…

【Springboot】新增profile环境配置应用启动失败

RT 最近接手了一个新的项目&#xff0c;为了不污染别人的环境&#xff0c;我新增了一个自己的环境配置。结果&#xff0c;在启动的时候总是失败&#xff0c;就算是反复mvn clean install也是无效。 问题现象 卡住无法进行下一步 解决思路 由于之前都是能启动的&#xff0c…

视频素材网站无水印的有哪些?热门视频素材网站分享

当我们走进视频创作的精彩世界时&#xff0c;一个难题常常摆在面前——那些高品质、无水印的视频素材究竟应该在哪里寻找&#xff1f;许多视频创作者感叹&#xff0c;寻找理想的视频素材难度甚至超过了寻找伴侣&#xff01;但不用担心&#xff0c;今天我将为您介绍几个优质的视…

宝塔安装RabbitMq教程

需要放开15672端口&#xff0c;默认账号密码为guest/guest

浅说区间dp(下)

文章目录 环形区间dp例题[NOI1995] 石子合并题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示思路 [NOIP2006 提高组] 能量项链题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示思路 [NOIP2001 提高组] 数的划分题目描述输入格式输出格式样例 #1样例输…

车载音视频App框架设计

简介 统一播放器提供媒体播放一致性的交互和视觉体验&#xff0c;减少各个媒体应用和场景独自开发的重复工作量&#xff0c;实现媒体播放链路的一致性&#xff0c;减少碎片化的Bug。本文面向应用开发者介绍如何快速接入媒体播放器。 主要功能&#xff1a; 新设计的统一播放U…

进程空间的回收以及执行当前进程空间内的另一进程

1.进程的退出 1.exit 功能: 让进程退出,并刷新缓存区 参数&#xff1a; status:进程退出的状态 返回值: 缺省 exit -> 刷新缓存区 -> atexit注册的退出函数 -> _exit 2._exit 功能: 让进程退出,不刷…

代码随想录——分割等和子集(Leetcode LCR 101)

题目链接 0-1背包问题 class Solution {public boolean canPartition(int[] nums) {int[] dp new int[10000];int sum 0;// 首先求背包体积应该为nums数组总和的一半for(int i 0; i < nums.length; i){sum nums[i];}// 如果总和为奇数则不存在等和子集if(sum % 2 1)…

输出调节求解跟踪问题(二阶线性系统)

本文研究了一种基于增广系统的领导者-跟随者控制框架&#xff0c;旨在实现跟随者系统对领导者参考信号的精确跟踪。首先&#xff0c;建立了跟随者和领导者的独立状态空间方程&#xff0c;分别描述了它们的动态行为和输出关系。随后&#xff0c;通过将两者的状态空间方程结合成增…

在Linux系统安装MySQL有多简单

MySQL 是一种流行的开源关系数据库管理系统&#xff0c;广泛应用于各种类型的应用程序和服务。在安装TitanIDE​​​​​​​以后是没有MySQL服务的&#xff0c;我们需要单独安装安装MySQL。本文将介绍在 Linux 上安装 MySQL 的多种方式&#xff0c;包括离线安装、使用 Docker …

el-tree动态添加子节点的问题

如果我们需要动态往el-tree里面某一个节点添加子节点&#xff0c;追加或删除&#xff0c;我跟你讲&#xff0c;一定要显式地调用el-tree的方法&#xff0c;不然的话&#xff0c;后面调用setChecked这种方法看不到效果的。 比如el-tree绑定的data如下&#xff1a; [{id:"1…

DP(1500-1700)(刷题)

1.状态机模型&#xff1a;https://codeforces.com/contest/1984/problem/C2 记一下max与min状态转移即可&#xff0c;下面是AC代码&#xff1a; #include<bits/stdc.h> using namespace std; typedef long long ll; ll a[200010],t,n; ll dp[200010][2];//dp[i][0]表示…

掌握这些技巧,让你成为画册制作高手

在数字化的时代背景下&#xff0c;电子画册以其便捷的传播方式、丰富的视觉表现形式&#xff0c;赢得了大众的喜爱。它不仅能够在个人电脑上展现&#xff0c;还能通过智能手机、平板电脑等多种移动设备随时随地被访问和浏览。这种跨平台的支持&#xff0c;使得无论你身处何地&a…

volatile关键字解析

一、volatile介绍 volatile是Java提供的一种轻量级的同步机制&#xff0c;在并发编程中&#xff0c;它也扮演着比较重要的角色。同synchronized相比&#xff08;synchronized通常称为重量级锁&#xff09;&#xff0c;volatile更轻量级&#xff0c;相比使用synchronized所带来的…

leetcode热题100.分割等和子集(动态规划)

分割等和子集 Problem: 416. 分割等和子集 思路 我选择使用动态规划的方法来解题。我们需要判断是否可以将数组分割成两个子集&#xff0c;使得这两个子集的和相等。这个问题可以转化为在数组中找到一个子集&#xff0c;使得其和等于数组总和的一半。 解题过程 首先&#xf…

【Django】网上蛋糕商城后台-商品管理

1.商品管理功能 当管理员点击商品管理时&#xff0c;发送服务器请求 path(admin/goods_list/, viewsAdmin.goods_list), # 处理商品列表请求 def goods_list(request):try:type request.GET["type"]except:type 0try:ym request.GET["ym"]except:ym …