C++ —— 类和对象(终)

目录

1. 日期类的实现

1.1 前置 ++ 和 后置 ++ 重载

1.2  >> 和 << 的重载

2. const 成员

 3. 取地址及const取地址操作符重载

4. 再谈构造函数 

4.1 构造函数体赋值

4.2 初始化列表

4.3 隐式类型转换

4.4 explict 关键字

5. static 成员

5.1 概念

 5.2 特性

6. 友元

6.1 友元函数

6.2 友元类

7. 内部类

8. 匿名对象 


1. 日期类的实现

 我在上一一篇文章详细写了日期类的计算器,链接如下:日期计算器icon-default.png?t=N7T8https://blog.csdn.net/m0_68617301/article/details/136856490

 主要讲两点:

1.1 前置 ++ 和 后置 ++ 重载

// 前置 ++ ,返回 ++ 之后的内容
Date& Date::operator++()
{*this += 1;return *this;
}// 后置 ++, 返回 ++ 之前的内容
Date Date::operator++(int)
{// 注意这里是拷贝构造,不是赋值重载Date tmp = *this;*this += 1;return tmp;
}
  • 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载,C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递。
  • 注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1而tmp是临时对象,因此只能以值的方式返回,不能返回引用

1.2  >> 和 << 的重载

  • 这里的 << 和 >> 的重载不能放在Date类里面,如果放在类里面,第一个参数默认就是this 指针,则 Date 类型的 参数是左操作数,与平时的用法不同。
  • 所以只能定义在类的外面,这个时候如果要访问类里面的 private 参数,可以通过友元的方法。
class Date
{
public:friend ostream& operator<<(ostream& _out, const Date& d);//友元friend istream& operator>>(istream& _in, Date& d);private:int _year;int _month;int _day;
};ostream& operator<<(ostream& _out, const Date& d)
{_out << d._year << "/" << d._month << "/" << d._day;return _out;
}istream& operator>>(istream& _in, Date& d)
{_in >> d._year >> d._month >> d._day;return _in;
}

2. const 成员

  • 如果定义了 const Date 的对象,再去调用 Print() 函数,会报错;
// 会报错
const Date d2(2022,1,13);
d2.Print();
  • 因为 默认会把 d2 的地址传过去,而 d2 的地址是 const Date* 的,this指针的类型的 Date* 的,会出现权限的放大;
  • 这时候可以把Print()函数后加上const就可以了,例如:
void Print() const
{//...
}
  • 这里的加const实际上是再this指针前加了const。
  1. const对象可以调用非const成员函数吗?         不可以,权限放大
  2. 非const对象可以调用const成员函数吗?          可以
  3. const成员函数内可以调用其它的非const成员函数吗?       不可以,权限放大
  4. 非const成员函数内可以调用其它的const成员函数吗?        可以

总的来说,如果是读功能的函数,可以加上const,而写功能的函数不加const。


 3. 取地址及const取地址操作符重载

class A
{
public:A* operator&(){return this;}const A* operator&()const{return this;}
private:
};

        这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容,可以返回假地址,例如

class A
{
public:A* operator&(){return nullptr;}const A* operator&()const{int a = 10;return (const A*)&a;}
};

4. 再谈构造函数 

4.1 构造函数体赋值

  • 在实例化对象的过程中,编译器会通过调用构造函数对成员变量进行赋值,例如:
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year; // 年int _month; // 月int _day; // 日
};
  • 构造函数体内容只能称作变量的赋值,不能说是变量的初始化,赋值可以有很多次但是初始化只能有一次,那是在什么时候初始化的呢?

4.2 初始化列表

  •  初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变                           量"后面跟一个放在括号中的初始值或表达式。
class Date
{
public:Date(int year, int month, int day):_year(year),_month(month),_day(day){}private:int _year; // 年int _month; // 月int _day; // 日
};

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

类中包含以下成员,必须放在初始化列表位置进行初始化:

  1. const 类型成员;
  2. 引用类型;
  3. 自定义类型(没有默认的构造函数)。
  • 所有的成员变量都会通过初始化列表进行定义,所以尽可能的在初始化列表中定义;
  • 对于自定义类型,如果没有默认构造函数(3种),且没有在初始化列表进行初始化,就会报错,因为它必须要进行初始化,而没有对应的构造函数调用。 
class B
{
public:/*B(int b = 1){this->b = b;}*/// 如果没有默认的构造函数会报错B(int b){this->b = b;}
private:int b;
};class A
{
public:A(int b,int raa):a(1),ra(raa)//,bb(b){}private:const int a;int& ra;B bb;
};

 看一下这段代码:

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

  • 输出结果是随机值,因为在初始化列表中,与你定义的顺序无关,和你在成员变量声明的顺序有关,这里是_a2先声明,_a1再声明,所以在初始化列表中也是先初始化_a2。

4.3 隐式类型转换

  • 先看如下代码
int main()
{int i = 10;double d = i;cout << d << endl;return 0;
}

  • 但是下面的一段代码就会报错:
int main()
{int i = 10;double& d = i;cout << d << endl;return 0;
}

 

  • 加个const就可以了
int main()
{int i = 10;const double& d = i;cout << d << endl;return 0;
}
  •  在类中也有类似的操作,如下的代码就是相当于先把 int 类型的 2 调用构造函数生成A类型的对象,之后再拷贝构造给 a。
class A
{
public :A(int a):a(a){}private:int a;
};int main()
{A a = 2;
}
  • 但是通常情况下,现在的编译器会做出相应的优化,例如我想测试编译器是否调用了拷贝构造,但是并没有任何的输出:
class A
{
public :A(int a):a(a){}A(const A& a){cout << "A(const A & a)" << endl;}private:int a;
};int main()
{A a = 2;
}

  • 这种情况就是相当于把 构造函数 + 拷贝构造 直接优化成了 构造函数
  • 如下的这种情况也是相同的道理,注意加上const,临时变量具有常性:
class A
{
public :A(int a):a(a){}A(const A& a){cout << "A(const A & a)" << endl;}private:int a;
};int main()
{// const 注意const A& b = 3;
}
  • 如果构造函数的时候有两个参数,在 C++ 11 后支持这样使用:
class A
{
public:A(int a, int b):a(a),b(b){}A(const A& a){cout << "A(const A & a)" << endl;}private:int a;int b;
};int main()
{A a = { 1,2 };
}

4.4 explict 关键字

  • 有的时候我们可能不想让构造函数进行隐式类型转换,这个时候可以在构造函数的函数名前加上 explicit 关键字就可以了 。
class A
{
public :explicit A(int a):a(a){}A(const A& a){cout << "A(const A & a)" << endl;}private:int a;
};int main()
{//这里就会报错const A& b = 3;A a = 2;
}

5. static 成员

5.1 概念

  •         声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化。
  • 我们可以利用static成员变量计算一共创建了几个对象:
class A
{
public:A(int a = 1, int b = 1):a(a),b(b){++c;}A(const A& a){cout << "A(const A & a)" << endl;++c;}static int Get(){return c;}private:int a;int b;static int c;
};int A::c = 0;int main()
{cout << A::Get() << endl;A a1, a2;A a3(a1);cout << A::Get() << endl;
}

 5.2 特性

  1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制

6. 友元

友元 是一种突破封装的方式

6.1 友元函数

就比如我刚才写的 >> 和 << 的 重载 就利用了友元 :

  •  这里的 << 和 >> 的重载不能放在Date类里面,如果放在类里面,第一个参数默认就是this 指针,则 Date 类型的 参数是左操作数,与平时的用法不同。
  • 所以只能定义在类的外面,这个时候如果要访问类里面的 private 参数,可以通过友元的方法。
class Date
{
public:friend ostream& operator<<(ostream& _out, const Date& d);//友元friend istream& operator>>(istream& _in, Date& d);private:int _year;int _month;int _day;
};ostream& operator<<(ostream& _out, const Date& d)
{_out << d._year << "/" << d._month << "/" << d._day;return _out;
}istream& operator>>(istream& _in, Date& d)
{_in >> d._year >> d._month >> d._day;return _in;
}
  • 友元函数可访问类的私有和保护成员,但不是类的成员函数
  • 友元函数不能用const修饰
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  • 一个函数可以是多个类的友元函数
  • 友元函数的调用与普通函数的调用原理相同

6.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;
};

7. 内部类

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

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

class A
{
private:static int k;int h;
public:class B // B天生就是A的友元{public:void foo(const A& a){cout << k << endl;//OKcout << a.h << endl;//OK}};
};
int A::k = 1;
int main()
{A::B b;b.foo(A());return 0;
}

8. 匿名对象 

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


 

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

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

相关文章

【Web】浅聊Hessian异常toString姿势学习复现

目录 前言 利用关键 调用分析 如何控制第一个字节 EXP 前言 Hessian CVE-2021-43297&#xff0c;本质是字符串和对象拼接导致隐式触发了该对象的 toString 方法&#xff0c;触发toString方法便可生万物&#xff0c;而后打法无穷也&#xff01; 这个CVE针对的是Hessian2I…

Follow-Your-Click——点选图像任意区域对象使用短提示语即可生成视频

简介 “I2V”&#xff08;图像到视频生成&#xff09;旨在将静态图像转换为具有合理动作的动态视频剪辑&#xff0c;在电影制作、增强现实和自动广告等领域有广泛应用。然而&#xff0c;现有的I2V方法存在一些问题&#xff0c;例如缺乏对图像中需要移动的部分的精准控制&#…

Chain of Verification-CoVe减少LLM中的幻觉现象

Chain-Of-Verification Reduces Hallucination In Large Language Models 在大型语言模型中&#xff0c;产生看似合理但实际上错误的事实信息&#xff0c;即幻觉&#xff0c;是一个未解决的问题。我们研究了语言模型在给出回答时进行深思以纠正错误的能力。我们开发了Chain-of…

深度解析 Android 系统属性

目录 Android系统属性 1.属性在哪里&#xff1f; 2.属性长什么样&#xff1f; 3.如何读写属性&#xff1a; 4.属性的作用 属性文件生成过程 如何添加系统属性 1.添加系统属性到 /system/build.prop 2.添加系统属性到 /vendor/build.prop 3.添加系统属性到 /product/b…

未来教育趋势:AI个性化培训如何推动企业与员工共赢

AI定制学习&#xff1a;重新定义个性化员工培训的未来 随着人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;我们正目睹并亲历了AI在培训领域所引发的根本性变革。AI技术的整合不仅革新了知识传递的模式&#xff0c;而且重新塑造了个性化学习的内涵。依托于尖端算…

《深入Linux内核架构》第2章 进程管理和调度 (4)

目录 2.6 CFS调度类 2.6.1 数据结构 2.6.2 CFS操作 2.6.3 队列操作 2.6.4 选择下一个进程 2.6.5 处理周期性调度器 2.6.6 唤醒抢占 2.6 CFS调度类 即完全公平调度类&#xff0c;用于调度普通进程。 2.6.1 数据结构 struct sched_class fair_sched_class {.next &am…

Restormer: Efficient Transformer for High-Resolution Image Restoration

Abstract 由于卷积神经网络&#xff08;CNN&#xff09;在从大规模数据中学习可概括的图像先验方面表现良好&#xff0c;因此这些模型已广泛应用于图像恢复和相关任务。最近&#xff0c;另一类神经架构 Transformer 在自然语言和高级视觉任务上表现出了显着的性能提升。虽然 T…

【安全类书籍-2】Web渗透测试:使用Kali Linux

目录 内容简介 作用 下载地址 内容简介 书籍的主要内容是指导读者如何运用Kali Linux这一专业的渗透测试平台对Web应用程序进行全面的安全测试。作者们从攻击者的视角出发,详细阐述了渗透测试的基本概念和技术,以及如何配置Kali Linux以适应渗透测试需求。书中不仅教授读者…

vulnhub-----SickOS靶机

文章目录 1.信息收集2.curl命令反弹shell提权利用POC 1.信息收集 ┌──(root㉿kali)-[~/kali/vulnhub/sockos] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:10:3c:9b, IPv4: 10.10.10.10 Starting arp-scan 1.9.8 with 256…

邮件客户端 Thunderbird 简单配置

1. 基本情况介绍 原来使用的邮箱客户端是 Office 365 自带的 Outlook 365切换原因&#xff1a;新装电脑&#xff0c;发现原 Outlook 中的账号信息无法迁移&#xff0c;需要耗费大量时间手动配置邮箱使用的邮箱&#xff1a;微软 O365 邮箱、qq 邮箱、163 邮箱、公司私有邮箱 …

北斗卫星引领农机春耕新时代

北斗卫星引领农机春耕新时代 随着现代科技的快速发展&#xff0c;北斗卫星成为了农业领域不可或缺的利器。在农机自动驾驶系统的引领下&#xff0c;农机正逐渐实现自主操作&#xff0c;为农民节省了大量的时间和精力&#xff0c;并最大限度地提高了农作物的产量和质量。 北斗…

MySQL | 内置函数

目录 1. 日期函数 2. 字符串函数 3. 数学函数 4. 其他函数 4.1. USER()查询当前用户 4.2. MD5(STR)对一个字符串进行md5摘要 4.3. DATABASE()显示当前正在使用的数据库 4.4. PASSWORD()函数&#xff0c;MySQL使用该函数对用户进行加密 4.5. IFNULL(VAL1, VAL2)如果VAL1…

window下安装并使用nvm(含卸载node、卸载nvm、全局安装npm)

window下安装并使用nvm&#xff08;含卸载node、卸载nvm、全局安装npm&#xff09; 一、卸载node二、安装nvm三、配置路径和下载源四、使用nvm安装node五、nvm常用命令六、卸载nvm七、全局安装npm、cnpm八、遇到的问题 nvm 全名 node.js version management&#xff0c;顾名思义…

Vivado中的五种仿真模式比较

Vivado中的五种仿真模式 在数字电路设计过程中&#xff0c;通常涵盖三个主要阶段&#xff1a;源代码编写、综合处理以及电路的实现&#xff0c;相应地&#xff0c;电路仿真的应用也与这些阶段紧密相关。根据不同设计阶段的需求&#xff0c;仿真可以被划分为RTL行为级仿真、综合…

练习4-权重衰减(李沐函数简要解析)

环境:练习1的环境 代码详解 0.导入库 import torch from torch import nn from d2l import torch as d2l1.初始化数据 这里初始化出train_iter test_iter 可以查一下之前的获取Fashion数据集后的数据格式与此对应 n_train, n_test, num_inputs, batch_size 20, 100, 200, …

三款.NET代码混淆工具比较分析:ConfuserEx、Obfuscar和Ipa Guard

随着.NET应用程序的广泛应用&#xff0c;保护知识产权和防止逆向工程的需求逐渐增长。本文将详细介绍三款知名的.NET代码混淆工具&#xff1a;ConfuserEx、Obfuscar和Ipa Guard&#xff0c;帮助读者全面了解其功能特点和应用场景。 一、ConfuserEx ConfuserEx是一个.NET代码混…

文件太大无法拷贝到u盘怎么办?可能是这个隐藏原因~

有时候我们新买的U盘&#xff0c;明明有64G的存储空间&#xff0c;怎么一个4.5G的视频想保存到U盘里会提示“文件太大”无法保存呢&#xff1f; 常见的U盘格式有FAT、FAT32、NTFS等&#xff0c;其中FAT32不支持存储单个文件体积大于4G的文件。下面教大家如何对U盘格式进行检测呢…

二开飞机机器人群发,实现自动给多个频道发送消息

频道1 频道2 二开代码部分&#xff1a; const CChatIdListprocess.env.CHANNEL_CHAT_ID_LIST; var channelChatIdArray CChatIdList.split(,);channelChatIdArray.forEach(function(item) {console.log(item); // 这里可以替换为您需要对数组中每个值进行的操作bot.sendM…

个人开发App成功上架手机应用市场的关键步骤

目录 1. 苹果审核和APP备案 2. APP上架操作步骤 3. 审核和发布 4. 上线工作 总结 参考资料 在当前移动应用市场竞争激烈的背景下&#xff0c;个人开发App如何成功上架成为开发者们必须面对的重要任务。本文将重点介绍自建App上架至手机应用市场的流程&#xff0c;包括苹果…

String类型详解

1. Java为何要创造String类 在C语言中,是没有String这个类型的,通常使用字符数组中存放一个个字符,再加上最后一个\0来表示/存放一个字符串.也可以使用一个字符指针指向字符串的首元素,直到遇到\0停止,再加上C语言头文件string.h中封装的函数,对于字符串的操作已经够用了. Java…