C++_类和对象(中、下篇)—— const成员函数、取地址运算符的重载、深入构造函数、类型转换、static成员、友元

目录

三、类和对象(中)

6、取地址运算符重载

1、const成员函数

2、取地址运算符的重载 

四、类和对象(下)

1、深入构造函数

2、类型转换

3、static成员 

4、友元


三、类和对象(中)

6、取地址运算符重载

1、const成员函数

  1. 将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后面。
  2. const实际修饰该成员函数隐含的this指针指向的对象,表明在该成员函数中不能对类的任何成员进行修改。没有修改的话就可以加,修改的话就不可以加const。
  3. const 修饰Date类的Print成员函数,Print隐含的this指针由 Date* const this 变为 const Date* const this
    #include<iostream>
    using namespace std;
    class Date
    {
    public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//若不加上const,则为void Print(Date* const this),this指针中的const修饰的是指针不能被改变// 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);//&d -> Date*d1.Print();const Date d2(2024, 8, 5);//&d -> const Date*d2.Print();return 0;
    }

总结:一个成员函数,不修改成员变量的建议都加上。 

c2821e9ff43843618ba4f742822423aa.png

更多详细例子可以去看博主的C++_类与对象(中篇)的日期类实现中的const修饰:C++_类和对象(中篇)—— 运算符重载、赋值运算符重载、日期类实现-CSDN博客

2、取地址运算符的重载 

  1. 取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载。
  2. ⼀般这两个函数编译器自动生成的就可以够我们用了,不需要去显示实现。
  3. 除非⼀些很特殊的场景,比如我们不想让别人取到当前类对象的地址,就可以自己实现⼀份,胡乱返回⼀个地址。
class Date
{
public :Date* operator&(){return this;// return nullptr;}const Date* operator&()const{return this;// return nullptr;}private :int _year ; // 年int _month ; // ⽉int _day ; // ⽇
};

四、类和对象(下)

1、深入构造函数

  1. 之前我们实现构造函数时,初始化成员变量主要使用函数体内赋值,构造函数初始化还有⼀种方式,就是初始化列表,初始化列表的使用方式是以⼀个冒号开始,接着是⼀个以逗号分隔的数据成员列表,每个"成员变量"后面跟⼀个放在括号中的初始值或表达式。
  2. 每个成员变量在初始化列表中只能出现⼀次,语法理解上初始化列表可以认为是每个成员变量定义初始化的地方。
  3. 引⽤成员变量,const成员变量,没有默认构造的类类型成员变量,这三种变量必须放在初始化列表位置进⾏初始化,否则会编译报错。
    #include<iostream>
    using namespace std;class Time
    {
    public:Time(int hour):_hour(hour){cout << "Time()" << endl;}private:int _hour;
    };class Date
    {
    public:Date(int& x, int year = 1, int month = 1, int day = 1):_year(year),_month(month),_day(day),_t(12),_ref(x),_n(1){// error C2512: “Time”: 没有合适的默认构造函数可⽤// error C2530 : “Date::_ref” : 必须初始化引⽤// error C2789 : “Date::_n” : 必须初始化常量限定类型的对象}void Print() const{cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;Time _t; // 没有默认构造int& _ref; // 引⽤const int _n; // const
    };int main()
    {int i = 0;Date d1(i);d1.Print();return 0;
    }
  4. C++11支持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显示在初始化列表初始化的成员使用的。
  5. 尽量使用初始化列表初始化,因为那些你不在初始化列表初始化的成员也会走初始化列表,如果这个成员在声明位置给了缺省值,初始化列表会用这个缺省值初始化。如果你没有给缺省值,对于没有显示在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++并没有规定。对于没有显示在初始化列表初始化的自定义类型成员会调用这个成员类型的默认构造函数,如果没有默认构造会编译错误。

    123dc7fdb22d4b3a8d179fb3b7c38b39.png

  6. 初始化列表中按照成员变量在类中声明顺序进行初始化,跟成员在初始化列表出现的的先后顺序无关。建议声明顺序和初始化列表顺序保持⼀致。
    #include<iostream>
    using namespace std;class Time
    {
    public:Time(int hour):_hour(hour){cout << "Time()" << endl;}private:int _hour;
    };class Date
    {
    public:Date():_month(2){cout << "Date()" << endl;}void Print() const{cout << _year << "-" << _month << "-" << _day << endl;}private:// 注意这⾥不是初始化,这⾥给的是缺省值,这个缺省值是给初始化列表的// 如果初始化列表没有显⽰初始化,默认就会⽤这个缺省值初始化int _year = 1;int _month = 1;int _day;Time _t = 1;const int _n = 1;int* _ptr = (int*)malloc(12);
    };int main()
    {Date d1;d1.Print();return 0;
    }

初始化列表总结:
  1. 无论是否显示写初始化列表,每个构造函数都有初始化列表;
  2. 无论是否在初始化列表显示初始化,每个成员变量都要走初始化列表初始化;

ccb5f5b9984240aea94d80a88842f929.png

思考:下面我们来看一道程序编程题目

2cca860b3e47446782287829644096b2.png c4badc7eae484b1794bcbe436dff7a1b.png

#include<iostream>
using namespace std;class A
{
public:A(int a):_a1(a), _a2(_a1){}void Print() {cout << _a1 << " " << _a2 << endl;}private:int _a2 = 2;int _a1 = 2;
};int main()
{A aa(1);aa.Print();
}

2、类型转换

  1. C++支持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。
  2. 构造函数前面加explicit就不再支持隐式类型转换。
  3. 类类型的对象之间也可以隐式转换,需要相应的构造函数支持。
    #include<iostream>
    using namespace std;class A
    {
    public:// 构造函数explicit就不再⽀持隐式类型转换// explicit A(int a1)A(int a1):_a1(a1){}//explicit A(int a1, int a2)A(int a1, int a2):_a1(a1), _a2(a2){}void Print(){cout << _a1 << " " << _a2 << endl;}int Get() const{return _a1 + _a2;}private:int _a1 = 1;int _a2 = 2;
    };class B
    {
    public:B(const A& a):_b(a.Get()){}private:int _b = 0;
    };//内置类型->自定义类型的转换(隐式类型的转化)
    //自定义类型->自定义类型转换(使用构造函数)
    int main()
    {// 1构造⼀个A的临时对象,再⽤这个临时对象拷⻉构造aa3// 编译器遇到连续构造+拷⻉构造->优化为直接构造A aa1 = 1;aa1.Print();const A& aa2 = 1;//拷贝的值放在临时变量中,具有常性,需要用const修饰,才能通过编译// C++11之后才⽀持多参数转化A aa3 = { 2,2 };// aa3隐式类型转换为b对象// 原理跟上⾯类似B b = aa3;//正常来说是不可以的const B& rb = aa3;//拷贝的值放在临时变量中,具有常性,需要用const修饰,才能通过编译A aa3 = {1,1};const A& aa4 = {1,1};//这是调用多参数的形式,本质都是隐式类型的转化return 0;
    }

    7f22b55accf843e2a78c49581dc5dc30.png

3、static成员 

  1. 用static修饰的成员变量,称之为静态成员变量,静态成员变量⼀定要在类外进行初始化。
  2. 静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区。
  3. 用static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针。
  4. 静态成员函数中可以访问其他的静态成员,但是不能访问非静态的,因为没有this指针。
  5. 非静态的成员函数,可以访问任意的静态成员变量和静态成员函数。
  6. 突破类域就可以访问静态成员,可以通过类名::静态成员 或者 对象.静态成员 来访问静态成员变量和静态成员函数。
  7. 静态成员也是类的成员,受public、protected、private 访问限定符的限制。
  8. 静态成员变量不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员变量不属于某个对象,不⾛构造函数初始化列表。
    // 实现⼀个类,计算程序中创建出了多少个类对象?
    #include<iostream>
    using namespace std;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;int main()
    {cout << A::GetACount() << endl;A a1, a2;A a3(a1);cout << A::GetACount() << endl;cout << a1.GetACount() << endl;// 编译报错:error C2248: “A::_scount”: ⽆法访问 private 成员(在“A”类中声明)//cout << A::_scount << endl;return 0;
    }

    思考:求1+2+3+...+n_牛客题霸_牛客网

2e250edfe62348769b269433bf3c823a.png

        既然题目要求不能使用这些方法,即循环累加、递归、等差数列公式等思路也行不通,C++解题模式下中提供的解决方案:使用Sum类和Solution类来解决,如下:

ead2fe9c15a04e2db8d2cd826c952c8c.png

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];//VS编译器不能通过//VS编译器可通过的写法:Sum* arr = new Sum[n];return Sum::GetRet();}
};
思考:
设已经有A,B,C,D 4个类的定义,程序中A,B,C,D构造函数调用顺序为?(选E)
设已经有A,B,C,D 4个类的定义,程序中A,B,C,D析构函数调用顺序为?(选B)
//以下为选项:
A:D B A C
B:B A D C
C:C D B A
D:A B D C
E:C A B D
F:C D A B//题目具体代码:
class A
{
public:A(){cout << "A()" << endl;}~A(){cout << "~A()" <<enl;}
};class B
{
public:B(){cout << "B()" << endl;}~B(){cout << "~B()" <<enl;}
};class C
{
public:C(){cout << "C()" << endl;}~C(){cout << "~C()" <<enl;}
};class D
{
public:D(){cout << "D()" << endl;}~D(){cout << "~D()" <<enl;}
};C c;int main()
{A a;B b;static D d;//构造:局部的静态,只有第一次找到初始化,才能够调用//析构:先析构局部变量(后面的先析构),后析构全局变量。return 0;
}

思路要点:

  1. 构造:局部静态变量,只有第一次找到初始化,才能够调用。
  2. 析构:先析构局部变量(后面的先析构),后析构全局变量。

VS运行编译结果:

2bff78a635634705bd954862ed787160.png

4、友元

  1. 友元提供了⼀种突破类访问限定符封装的方式,友元分为:友元函数和友元类,在函数声明或者类声明的前面加friend,并且把友元声明放到⼀个类的里面。
  2. 外部友元函数可访问类的私有和保护成员,友元函数仅仅是⼀种声明,他不是类的成员函数。
  3. 友元函数可以在类定义的任何地方声明,不受类访问限定符限制。
  4. ⼀个函数可以是多个类的友元函数。
  5. 友元类中的成员函数都可以是另⼀个类的友元函数,都可以访问另⼀个类中的私有和保护成员。
  6. 友元类的关系是单向的,不具有交换性,比如A类是B类的友元,但是B类不是A类的友元。
  7. 友元类关系不能传递,如果A是B的友元, B是C的友元,但是A不是C的友元。
  8. 有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用(项目程序中希望是低耦合和高内聚)。
    #include<iostream>
    using namespace std;// 前置声明,都则A的友元函数声明编译器不认识B
    class B;class A
    {// 友元声明friend void func(const A& aa, const B& bb);private:int _a1 = 1;int _a2 = 2;
    };class B
    {// 友元声明friend void func(const A& aa, const B& bb);private:int _b1 = 3;int _b2 = 4;
    };void func(const A& aa, const B& bb)
    {cout << aa._a1 << endl;cout << bb._b1 << endl;
    }int main()
    {A aa;B bb;func(aa, bb);return 0;
    }
    #include<iostream>
    using namespace std;class A
    {// 友元声明friend class B;
    private:int _a1 = 1;int _a2 = 2;
    };class B
    {
    public:void func1(const A& aa){cout << aa._a1 << endl;cout << _b1 << endl;}void func2(const A& aa){cout << aa._a2 << endl;cout << _b2 << endl;}private:int _b1 = 3;int _b2 = 4;
    };int main()
    {A aa;B bb;bb.func1(aa);bb.func1(aa);return 0;
    }

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

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

相关文章

削峰+限流:秒杀场景下的高并发写请求解决方案

我是小米,一个喜欢分享技术的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货! 哈喽,大家好!我是小米,一个29岁、活泼积极、热衷分享技术的码农。今天和大家聊一聊应对高并发的写请求这个主题,尤其是在大促、秒杀这种场景下,系统…

南京信息工程大学《2020年+2021年817自动控制原理真题》 (完整版)

本文内容&#xff0c;全部选自自动化考研联盟的&#xff1a;《25届南京信息工程大学817自控考研资料》的真题篇。后续会持续更新更多学校&#xff0c;更多年份的真题&#xff0c;记得关注哦~ 目录 2020年真题 2021年真题 Part1&#xff1a;20202021年完整版真题 2020年真题…

Unity3D下如何播放RTSP流?

技术背景 在Unity3D中直接播放RTSP&#xff08;Real Time Streaming Protocol&#xff09;流并不直接支持&#xff0c;因为Unity的内置多媒体组件&#xff08;如AudioSource和VideoPlayer&#xff09;主要设计用于处理本地文件或HTTP流&#xff0c;而不直接支持RTSP。所以&…

并查集的应用

目录 1.并查集的代码 2.union操作 3.find操作 4.图 写代码&#xff1a;定义一个并查集&#xff08;用长度为n的数组实现&#xff09; 基于上述定义&#xff0c;实现并查集的基本操作—— 并 Union 基于上述定义&#xff0c;实现并查集的基本操作—— 查 Find 自己设计一…

欧美游戏市场的差异

欧洲和美国的游戏市场虽然高度发达且利润丰厚&#xff0c;但表现出由文化偏好、消费者行为、监管环境和平台受欢迎程度塑造的独特特征。这些差异对于寻求为每个地区量身定制策略的游戏开发商和发行商来说非常重要。 文化偏好和游戏类型 美国&#xff1a;美国游戏市场倾向于青…

Java基础尚硅谷84-面向对象-package与import关键字的使用

曾国藩说&#xff0c;基础不牢&#xff0c;很难走得远。 所以时时回顾一下Java基础&#xff0c;打好地基&#xff0c;让自己走得更稳&#xff0c;更远。 今天这节课&#xff0c;学到对自己有点价值的东西是&#xff1a; 1 import 导入A包.*&#xff0c;可以使用A包下&#xff…

操作系统、数据库

操作系统 管道&#xff1a;读写先进先出&#xff0c;不能想读哪里就读哪里&#xff0c;想写哪里就哪里 内存 块表的理论支撑&#xff1a;局部性原理&#xff1a;1.时间局部性和空间局部性原理。 内存映射&#xff1a;

目标检测——flask后端YOLOv8检测视频,前端实时显示检测结果

前端代码&#xff1a; index.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>YOLOv8 Video S…

网络安全笔试练习题,据说10分钟内答对的都是高手!

《网安面试指南》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484339&idx1&sn356300f169de74e7a778b04bfbbbd0ab&chksmc0e47aeff793f3f9a5f7abcfa57695e8944e52bca2de2c7a3eb1aecb3c1e6b9cb6abe509d51f&scene21#wechat_redirect 《Java代码审…

CSP-J/S赛前知识点大全2:初赛纯靠记忆的知识点

-NOI的中文意思是&#xff08;全国青少年信息学奥林匹克竞赛&#xff09;。 -NOIP从&#xff08;2022&#xff09;年开始不支持Pascal语言。 -中国计算机学会&#xff08;CCF&#xff09;于&#xff08;1962&#xff09;年成立&#xff0c;于(1984)年创办全国青少年计算机程序设…

惬意享受阅读,优雅的微信公众号订阅方式,极空间部署『WeWe RSS』

惬意享受阅读&#xff0c;优雅的微信公众号订阅方式&#xff0c;极空间部署『WeWe RSS』 哈喽小伙伴们好&#xff0c;我是Stark-C~ 不知道大家平时是怎么阅读自己关注的公众号文章的&#xff0c;是不是基本就靠微信平自动提醒更新呢&#xff1f;如果是这样&#xff0c;那么我…

dubbo二

dubbo dubbo扩展加载流程 服务调用过程 线程派发模型 多版本控制 集群容错 策略对比 负载均衡及其实现

ICM20948 DMP代码详解(25)

接前一篇文章&#xff1a;ICM20948 DMP代码详解&#xff08;24&#xff09; 上一回讲到了inv_icm20948_load_firmware函数&#xff0c;对于大体功能进行了介绍&#xff0c;本回深入其具体实现代码细节。为了便于理解和回顾&#xff0c;再次贴出相关代码&#xff1a; //Setup Iv…

甲骨文创始人埃里森:人工智能终有一天会追踪你的一举一动

9月17日消息&#xff0c;据外电报道&#xff0c;甲骨文创始人拉里埃里森在甲骨文财务分析师会议上表示&#xff0c;他预计人工智能有一天将为大规模执法监控网络提供动力。“我们将进行监督。”他说。“每一位警察都将随时受到监督&#xff0c;如果有问题&#xff0c;人工智能会…

从0到一个漏洞几千块,走了这么久,走了这么远,当然还要继续走下去。

从0到一个漏洞几千块&#xff0c;走了这么久&#xff0c;走了这么远&#xff0c;当然还要继续走下去。

odb使用

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、创建学生类和班级类1.学生类2.班级类3.生成数据库支持代码 二、创建数据库对象&#xff0c;对数据库进行操作1.构建连接池工厂配置对象2.构造数据库操作对象…

概率分布深度解析:PMF、PDF和CDF的技术指南

本文将深入探讨概率分布&#xff0c;详细阐述概率质量函数&#xff08;PMF&#xff09;、概率密度函数&#xff08;PDF&#xff09;和累积分布函数&#xff08;CDF&#xff09;这些核心概念&#xff0c;并通过实际示例进行说明。 在深入探讨PMF、PDF和CDF之前&#xff0c;有必…

JavaSE - 面向对象编程03

01 多态 01_01 认识多态 01_02 多态的好处和缺点 【1】好处&#xff1a;① 可以解耦合&#xff0c;扩展性更强&#xff0c;父类引用指向的子类对象可以随时切换&#xff0c;而后面的逻辑 代码并不需要更改。 ② 使用父类引用可以作为方法的形参或返…

java138-异常处理_java 138错误

//异常 public class test79 { //定义方法声明定义异常&#xff0c;在满足条件时抛出异常对象&#xff0c;程序转向异常处理 public double count(double n,double m)throws Exception { if (m 0) {//如果除数等于0.则抛出异常实例 throw new Ex…

C/C++:优选算法(持续更新~~)

一、双指针 1.1移动零 链接&#xff1a;283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操…