C++类和对象下篇

在这里插入图片描述

🐇

🔥博客主页: 云曦
📋系列专栏:[C++]

💨路漫漫其修远兮 吾将而求索
💛 感谢大家👍点赞 😋关注📝评论

文章目录

  • 📔1、再谈构造函数
    • 📰1.1、构造函数体赋值
    • 📰1.2、初始化列表
    • 📰1.3、explicit关键字
  • 📔2、static成员
    • 📰2.1、概念
    • 📰2.2、特性
  • 📔3、友元
    • 📰3.1、友元函数
    • 📰3.2、友元类
  • 📔4、内部类
  • 📔5、匿名对象
  • 📔6、拷贝对象时编译器的一些优化

📔1、再谈构造函数

📰1.1、构造函数体赋值

  • 在实例化对象时,编译器调用构造函数,在函数体内对成员变量进行赋值。
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};
  • 虽然在实例化对象时给成员变量进行了赋值,但这并不是对成员变量的初始化,而是在初始化完后给成员变量赋值。

📰1.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.每个成员变量初始化只能出现一次(初始化只能初始化一次)。2.类里面出现以下的成员变量,只能在初始化列表进行初始化。(1)引用成员变量。(2)const成员变量(3)自定义类型成员(且没有默认构造函数时)
class A
{
public:A(int a):_a(a){}private:int _a;
};class Date
{
public:Date(int year, int month, int day):_year(year), _month(month), _day(day),_ret(day),_i(10),aa(20){}private:int _year;int _month;int _day;int& _ret;     //引用 -> 语法规定:引用的对象必须初始化const int _i; //const成员变量 -> 语法规定const的类型必须初始化A aa;//没有默认构造 -> 成员变量是自定义类型时会去调用对应的默认构造,如果没有就会报错};
(3)尽量使用初始化列表初始化,因为不管你用不用初始化列表,编译器都会走初始化列表。
class Time
{
public:Time(int hour = 10):_hour(hour){cout << "Time(int hour = 10)" << endl;}private:int _hour;
};class Date
{
public:Date(int year){}private:int _year;Time time_t;
};int main()
{Date d1(2000);return 0;
}

在这里插入图片描述

(4)成员变量在类中的次序就是初始化列表的次序,与初始化列表的次序无关。
class Date
{
public:Date(int year = 1, int month = 1, int day = 1): _day(day), _month(month),_year(year){}private:int _year;int _month;int _day;
};int main()
{Date d1;return 0;
}

初始化列表的执行次序

  • 成员变量的次序与初始化列表次序不同会带来的问题
class stack
{
public:stack(size_t n): _size(0), _capacity(n), _a((int*)malloc(sizeof(_a)* _capacity)){if (_a == nullptr){perror("malloc fail");exit(-1);}}void push(int val){_a[_size] = val;_size++;}~stack(){free(_a);_a = nullptr;_size = 0;_capacity = 0;}private:int* _a;size_t _size;size_t _capacity;
};int main()
{stack st(10);st.push(1);st.push(2);st.push(3);//程序崩溃//原因:malloc开空间时,计算所需的字节是用_capacity进行计算的//但初始化列表的次序是跟着成员变量的次序走的,_capacity在_a的后面//那么先走的就是对_a进行开空间,这时_capacity没有初始化,是随机值//然后空间就有可能没有开对导致插入数据时程序崩溃return 0;
}
  • 所以大家在用初始化列表时,建议把初始化列表的次序和成员变量的次序写成一致,即实现了代码的可读性又避免了不必要的麻烦!

📰1.3、explicit关键字

  • 构造函数不止具有构造和初始化对象的功能,对于单个参数或多个参数只有第一个参数没有缺省值其余均有缺省值的情况,还具有类型转换的作用。(简单来说就是单参数的构造函数支持隐式类型转换)
class Date
{
public:Date(int year, int month = 1, int day = 1): _day(day), _month(month),_year(year){}private:int _year;int _month;int _day;
};int main()
{Date d1(2023);//一个整型可以赋值给一个对象初始化//都是源自于单参数的构造函数支持隐式类型转换d1 = 2024;return 0;
}

在这里插入图片描述

  • explicit的作用就是不让他修饰的函数进行隐式类型转换
class Date
{
public:explicit Date(int year, int month = 1, int day = 1): _day(day), _month(month), _year(year){}private:int _year;int _month;int _day;
};int main()
{Date d1(2023);d1 = 2024;
//编译报错: C2679: 二元“=”: 没有找到接受“int”类型的右操作数的运算符(或没有可接受的转换)return 0;
}

📔2、static成员

📰2.1、概念

(1)用stack修饰的类成员称为类的静态成员。
(2)用stack修饰的成员变量称为静态成员变量。
(3)用stack修饰的成员函数称为静态成员函数。注意:定义静态成员变量时,必须在类外进行初始化。
class A
{
public:A(int a = 0):_a(a){}int Get(){return _a;}private:int _a;
};class B
{
public:static void Print()  //静态成员函数{cout << _aa.Get() << endl;cout << _b << endl;}private:static A _aa;//类的静态成员static int _b;//静态成员变量
};//静态成员变量需要在类外初始化
int B::_b = 10;
A B::_aa = 20;int main()
{A aa1;B::Print();//静态成员函数的调用return 0;
}
  • 那么static修饰的对象究竟有什么用了?
  • 这里有一个问题可以很好的讲解static的作用;
  • 实现一个类,计算出程序里创建出了多少个类对象
class A
{
public:A(){A::_count++;}A(const A& a){A::_count++;}~A(){A::_count--;}static int GetCount(){return _count;}private:static int _count;
};
这里分两种情况:
(1) 如果GetCount函数不是静态成员函数
int main()
{//3个类对象A aa1;A aa2(aa1);A aa3;//如果GetCount函数不是静态成员函数//编译报错:error C2352: // “A::GetCount”: 调用非静态成员函数需要一个对象cout << A::GetCount() << endl; //GetCount函数不是静态成员函数//多定义一个对象用这个对象.调用//打印时要-1因为这个a这个对象//是我们实例化出来调用GetCount静态成员函数的A a;cout << a.GetCount() - 1 << endl;return 0;
}
(2) 如果GetCount函数是静态成员函数
int main()
{//3个类对象A aa1;A aa2(aa1);A aa3;//如果GetCount函数是静态成员函数//即可以通过一个对象调用A a;cout << a.GetCount() - 1 << endl;//也可以用类名指定调用cout << A::GetCount() << endl;return 0;
}
  • 具体为何GetCount不是静态成员函数就不能用类名指定调用,且看static的特性。

📰2.2、特性

  1. 静态成员被所有类对象共享,不属于任何一个对象,存在静态区
  2. 静态成员变量必须在类外定义,定义时不加static关键字,类内只是声明
  3. 类的静态成员可以用 类名::静态成员 或者 对象.静态成员 进行访问
  4. 静态成员函数没有this指针,不能访问非静态成员
  5. 静态成员本质也是类的成员,受public、protected、private访问限定符限制
注意:(1) 静态成员函数不可以调用非静态成员函数。(2) 非静态成员函数可以调用静态成员函数
class A
{
public:A(int a = 0):_a(a){}static void func1(){func2();//编译报错:error C2352: //“A::func2”: 调用非静态成员函数需要一个对象}void func2(){func1();//程序正常运行}private:int _a;
};int main()
{A aa(10);return 0;
}

📔3、友元

  • 友元提供了一种突破封装的方式,有时提供了便利,但友元会增加耦合度,破坏了封装,所以友元不宜多用。
  • 耦合度讲解:
    在这里插入图片描述
  • 友元分为友元函数友元类

📰3.1、友元函数

  • 在类和对象中篇,讲解operator<<和operator>>重载时,因为实现成成员函数时,this指针占用了第一个参数导致cout无法成为第一个参数,所以只能把operator<<和operator>>重载实现在类外,但类外无法访问到类内的成员变量,这时用友元解决了这里的问题。
class Date
{friend ostream& operator<<(ostream& out, Date& d);friend istream& operator>>(istream& in, 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, 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(2024,6,2);cout << d1 << endl;return 0;
}
  • 友元函数可以直接访问类内的私有成员,定义在类外的函数需要在类内声明且声明时需要加上friend关键字。
  • 注意:
    (1) 友元函数可以访问类内的私有和保护成员,但不能访问类内的成员函数
    (2) 友元函数不能用const修饰
    (3) 友元函数可以定义在类定义的任何地方,不受访问限定符限制。
    (4) 一个函数可以是多个类的友元函数
    (5) 友元函数的调用与普通函数调用是一样的

📰3.2、友元类

友元类的所有成员函数都可以访问另一个类中的非公有成员(公有成员类内类外都可以访问)。

  • 友元类的关系是单相的,不具有交换性
class A
{
//声明B类是A类的友元,B类可以访问A类的私有成员变量,
//但A类无法访问B类的私有成员friend class B;
public:A(int a = 100):_a(a){}void Print(){cout << "_a:" << _a << endl;cout << "_b:" << bb._b << endl;//编译报错://error C2079: “A::bb”使用未定义的 class“B”}private:int _a;B bb;
};class B
{
public:B(int b = 100):_b(b){}void Print(){cout << "_a:" << aa._a << endl;cout << "_b:" << _b << endl;}private:int _b;A aa;
};
  • 友元关系不能传递

举例:

  • 有A、B、C三个类,B类是A类的友元,C类是B类的友元,在A类里没有C类友元声明的情况下,C类不会是A类的友元。
class A
{friend class B;
public:A(int a = 0):_a(a){}private:int _a;
};class B
{friend class C;
public:B(int b = 0):_b(b){}void funB(){_aa._a;//B是A的友元,可以访问A类的私有成员变量}private:int _b;A _aa;
};class C
{
public:C(int c = 0):_c(c){}void funC(){_bbb._b;//C是B的友元,可以访问B类的私有成员变量_aaa._a;//C不是A的友元,不可以访问A类的私有成员变量//编译报错://error C2248: “A::_a”: //无法访问 private 成员(在“A”类中声明)}private:int _c;A _aaa;B _bbb;
};
  • 友元关系不能继承
    这里关联到了继承,等到继承篇再和大家讲解。

📔4、内部类

  • 概念:在一个类内定义的类称为内部类,内部类就是一个独立的类,它不属于外部类,外部类对内部类没有任何优越的访问权限。
  • 注意:内部类天生就是外部类的友元,内部类可以访问外部类的私有成员变量,但外部类无法访问内部类的私有成员变量。
class A
{
public:class B{public:B(int b = 0):_b(b){}void funB(const A& aa)//B天生是A的友元{aa._a;}private:int _b;};A(int a = 0):_a(a){}private:int _a;
};
  • 特性:
    1. 内部类可以定义在外部类的public、protected、private都是可以的。
class A
{
public:class B{public:void funa(const A& aa){aa._a;}protected:void funb(const A& aa){aa._a;}private:void func(const A& aa){aa._a;}private:int _b;};private:int _a;
};
  1. 内部类可以访问外部类的static修饰的成员,不用指定类名或外部类的对象。
class A
{
public:class B{public:void func(const A& aa){cout << aa._a << endl;cout << _n << endl;}private:int _b;};private:int _a;static int _n;
};int A::_n = 0;
  1. sizeof(外部类)不会计算内部类,和内部类没有任何关系。
class A
{
public:class B{public:void func(const A& aa){cout << aa._a << endl;}private:int _b;};private:int _a;//4字节
};int main()
{cout << sizeof(A) << endl;//编译通过,打印4return 0;
}

📔5、匿名对象

class A
{
public:A(int a = 0):_a(a){cout << "A(int a = 0)" << endl;}~A(){cout << "~A()" << endl;}private:int _a;
};int main()
{A aa1(10);//实例化对象//匿名对象//匿名对象的特点就是不用给对象起名字//且匿名对象的生命周期只在这一行//过了这一行就会调用析构函数A(20);A aa2(1);return 0;
}
  • 匿名对象的使用场景
class A
{
public:int A_func(int n){//进行累加之类的操作...return n;}
};int main()
{A().A_func(10);return 0;
}

STL篇章还会有其他的使用场景。

📔6、拷贝对象时编译器的一些优化

  • 在传参和传返回值时,现在的编译器一般都会做的一些优化,这些优化在一些场景下还很有用。
  • **注意:**我这里用的是vs2022来演示的,不同的编译器不同,可能会优化也可能不优化。
  • 这个优化就是:在同一个表达式内:
  1. 构造+构造 -> 构造(两次构造会优化成一次构造)
  2. 构造+拷贝构造->构造(一次构造和一次拷贝构造会优化成一次构造)
  3. 拷贝构造+拷贝构造->拷贝构造(两次拷贝构造会优化成一次拷贝构造)
class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}A(const A& a){cout << "A(const A& a)" << endl;_a = a._a;}A& operator=(const A& a){cout << "A& operator=(const A& a)" << endl;if (this != &a){_a = a._a;}return *this;}~A(){cout << "~A()" << endl;}private:int _a;
};
  • 场景1
void func1(A aa)
{}void scene1()
{//单参数的构造函数支持隐式类型转换//隐式类型转换,构造一个临时对象//用这个临时对象再去拷贝构造给aa//构造+拷贝构造->优化成一个构造func1(10);
}

在这里插入图片描述

  • 场景3

A func2()
{A aa(10);return aa;
}void scene2()
{//func2函数的aa对象是一个局部对象//传值返回这个对象会拷贝构造出一个临时对象返回//返回的临时对象再去拷贝构造a2//连续的拷贝构造+拷贝构造->被优化成一次拷贝构造A a2 = func2();
}
  • 场景3
A func2()
{A aa(10);return aa;
}void scene3()
{//传值返回,拷贝构造一个临时对象//再用这个临时对象去赋值拷贝给a3//拷贝构造 + 赋值拷贝 -> 无法优化A a3;a3 = func2();
}

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

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

相关文章

【C++】类和对象——构造和析构函数

目录 前言类的六个默认构造函数构造函数1.构造函数的概念2.构造函数的特性 初始化列表1.构造函数整体赋值2.初始化列表 析构函数1.析构函数的概念2.析构函数的特性 前言 类和对象相关博客&#xff1a;【C】类和对象   我们前面一个内容已经讲了关于类好对象的初步的一些知识&…

Python量化交易学习——Part5:通过相关系数选择对收益率影响比重大的因子(1)

上一节中我们学习了如何通过单因子策略进行股票交易,在实际的股市中,因子(也就是指标)数量往往非常之多,比如市盈率/市净率/净资产收益率等,在使用这些因子的过程中,我们会发现有的因子与收益率为正相关,有的因子为负相关,而有些因子几乎完全无关。 所以我们可以通过计…

JS-10-es6常用知识-对象扩展

目录 1 Object.assign&#xff1a;实现拷贝继承 2 扩展运算符(...) 1&#xff09;介绍 2&#xff09;数组中的扩展运算符 3&#xff09;对象中的扩展运算符 1 Object.assign&#xff1a;实现拷贝继承 1&#xff09;目的&#xff1a;Object.assign()方法在 JavaScript 中被…

Flutter开发效率提升1000%,Flutter Quick教程之定义构造参数和State成员变量

一个Flutter页面&#xff0c;可以定义页面构造参数和State成员变量。所谓页面构造参数&#xff0c;就是当前页面构造函数里面的参数。 比如下面代码&#xff0c;a就是构造参数&#xff0c;a1就是State成员变量。 class Testpage extends StatefulWidget {String a;const Test…

Python的文件管理

读取文件 首先我们可以先创建一个工程项目&#xff0c;如图所示&#xff1a; 打开我们名为1.读取文件.py的python文件&#xff0c;然后我们可以写下读取Python文件的代码&#xff0c;代码如下&#xff1a; f open("1.txt", "r") print(f.read()) f.clos…

【PB案例学习笔记】-14使用次数和日期限制

写在前面 这是PB案例学习笔记系列文章的第14篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…

【工具】探索 ARPU:平均每用户收入

缘分让我们相遇乱世以外 命运却要我们危难中相爱 也许未来遥远在光年之外 我愿守候未知里为你等待 我没想到为了你我能疯狂到 山崩海啸没有你根本不想逃 我的大脑为了你已经疯狂到 脉搏心跳没有你根本不重要 &#x1f3b5; 邓紫棋《光年之外》 什么是 ARP…

UE5.1_常用快捷键

UE5.1_常用快捷键 shift1&#xff0c;&#xff0c;模式选择 shift2&#xff0c;&#xff0c;模式选择 shift3&#xff0c;&#xff0c;模式选择 shift4&#xff0c;&#xff0c;模式选择 shift5&#xff0c;&#xff0c;模式选择 shift6&#xff0c;&#xff0c;模式选择 …

2.3Docker部署java工程

2.3Docker部署java工程 1.导入jar包 2.在Docker部署jdk&#xff08;容器名为myjdk17&#xff09; 3.修改jar包名 mv 原包名 新包名4. 配置启动脚本 Dockerfile是一个文本文件&#xff0c;其中包含了构建 Docker 镜像所需的一系列步骤和指令。通过编写 Dockerfile 文件&…

Adversarial Nibbler挑战:与多元社区持续开展开放红队测试

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

RPG Maker MV角色战斗动画记录

角色战斗动画记录 角色战斗状态判断的语句赋值 战斗管理战斗精灵创建精灵进行角色的更新 角色战斗状态 角色的战斗状态是由 Game_Battler 类中的 _actionState 属性的字符串决定的&#xff0c;它有哪些值呢&#xff1f; undecided 未确定或者说是操作状态inputting 输入waiti…

QA 未能打开位于 D:/Computer999/Computer999.vbox 的虚拟电脑

前言 未能打开位于 xxx/Computer999.vbox 的虚拟电脑&#xff0c;并提示E_INVALIDARG (0X80070057)&#xff0c;是最常见的一个错误&#xff0c;下面是解决办法。 内容 1、提示下面的错误&#xff0c;注册Computer999失败&#xff1a; 未能打开位于 D:/Computer999/Compute…

K210视觉识别模块学习笔记1:第一个串口程序_程序烧录与开机启动

今日开始学习K210视觉识别模块:简单的认识与串口程序 亚博智能的K210视觉识别模块...... 固件库版本: canmv_yahboom_v2.1.1.bin 既然K210作为一个视觉识别外设模块来使用&#xff0c;我认为第一个程序 就没必要学点灯之类的了&#xff0c;直接学习串口如何配置开始为妥&…

ctfshow-web入门-爆破(web21-web24)

目录 1、web21 2、web22 3、web23 4、web24 1、web21 爆破什么的&#xff0c;都是基操 需要认证才能访问 随便输一个用户名和密码抓包看看&#xff1a; 多出来一个认证的头 Authorization: Basic YWRtaW46MTIzNDU2 base64 解码看看&#xff1a; 就是我们刚才输入的用于测…

C语言 | Leetcode C语言题解之第127题单词接龙

题目&#xff1a; 题解&#xff1a; struct Trie {int ch[27];int val; } trie[50001];int size, nodeNum;void insert(char* s, int num) {int sSize strlen(s), add 0;for (int i 0; i < sSize; i) {int x s[i] - ;if (trie[add].ch[x] 0) {trie[add].ch[x] size;m…

计算机系统结构之FORK和JOIN

程序语言中用FORK语句派生并行任务&#xff0c;用JOIN语句对多个并发任务汇合。 FORK语句的形式为FORK m&#xff0c;其中m为新领程开始的标号。 JOIN语句的形式为JOIN n&#xff0c;其中n为并发进程的个数。 例1&#xff1a;给定算术表达式ZEA*B*C/DF经并行编译得到如下程序…

刘强东的简历很拉风!

正式宣布&#xff1a;GPT 4o 在国内直接使用 ~ 来看一下江湖人称“东哥”刘强东的简历&#xff0c;大佬确实很拉风&#xff1a; 刘强东&#xff0c;京东的创始人&#xff0c;是中国互联网行业的传奇人物。他的故事充满了奋斗和创新&#xff0c;以下是我对他简历的一些看法&…

Vitis HLS 学习笔记--HLS流水线类型

目录 1. 简介 2. 优缺点对比 2.1 Stalled Pipeline 2.2 Free-Running/Flushable Pipeline 2.3 Flushable Pipeline 3. 设置方法 4. FRP的特殊优势 5. 总结 1. 简介 Vitis HLS 会自动选择正确的流水线样式&#xff0c;用于流水打拍函数或循环。 停滞的流水线&#xff…

K8S SWCK SkyWalking全链路跟踪工具安装

官方参考&#xff1a;如何使用java探针注入器? 配置两个demo&#xff0c;建立调用关系&#xff0c; 首先创建一个基础镜像dockerfile from centos 先安装java 参考: linux rpm方式安装java JAVA_HOME/usr/java/jdk1.8.0-x64 CLASSPATH.:$JAVA_HOME/lib/tools.jar PATH…

了解Maven,并配置国内源

目录 1.了解Maven 1.1什么是Maven 1.2快速创建一个Maven项⽬ 1.3Maven 核⼼功能 1.3.1项⽬构建 1.3.2依赖管理 1.4Maven Help插件 2.Maven 仓库 2.1中央仓库 2.2本地仓库 3.Maven 设置国内源 1.查看配置⽂件的地址 2.配置国内源 3.设置新项⽬的setting 1.了解Ma…