【C++初阶】类和对象(下) ╰+攻爆jí腚メ 2024-04-22 12:10 34阅读 0赞 ![26e580bde9144fe2a5d9b8690ac2614b.gif][] ## 一.再谈构造函数 ## > 构造函数其实分为: > > **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.const 变量** > > **2.引用变量** > > **3.自定义变量** > > 接下来我们一个一看。 **const 变量** ![1ac4d8876443439e9cda6b8c23908238.png][] 可以看到在函数体中对 const 变量是不可以初始化的,所以必须要在初始化列表中初始化; **引用变量** ![a14dc58b786842ce9c0083e4dd5beeea.png][] 很明显,对于引用变量也不能在函数体中初始化;这里还要注意给引用传参时,也要传引用,否则会出现类似野引用的情况,这种情况很危险。 **自定义变量** 对于自自定义变量,**会去调用它的默认构造函数**,所以不显式初始化自定义变量也行,但如果该自定义变量**没有默认构造函数的话,就必须要显式初始化**(关于默认构造函数:[构造函数和析构函数][Link 1]) ![eca7af87e2134b988b7b42bba7145630.png][] 如上图所示,对于没有默认构造函数的自定义变量,因为未显示初始化,所以编译器报了错。 #### 初始化列表的一个坑 #### 我们先来看一段代码: 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 随机值 > > 为什么? > > 这就不得不说到初始化列表的一个有点坑的地方了。 > **成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后 > 次序无关;** > > 也就是说,上述代码的初始化列表中,先初始化的是 \_a2 变量,而 \_a2 变量是初始化成 \_a1 变量的,但此时 \_a1 变量还没有初始化,所以就出现了随机值。 > > 所以呢,初始化列表时最好**按照声明的顺序初始化**。 #### 总结 #### > 1.**初始化列表其实是成员变量定义的地方,不管有没有写都会走一遍,且也只会走一遍**; > > private 中的其实是成员变量的声明; > > 2.**尽量使用初始化列表初始化**,因为不管你是否使用初始化列表,对于自定义类型成员变 量,一定会先使用初始化列表初始化。 > > 3.**初始化列表并不能完全代替函数体赋值**。 -------------------- ## 二.explicit 关键字 ## ### 内置类型与自定义类型的隐式类型转换 ### 先看这样一段1代码 class A { public: A(int a=1) :_a(a) {} private: int _a; }; int main() { A a1(1); A a2 = 2; //这句代码有问题吗 return 0; } > 我们发现了一个令人有点摸不着头脑的代码: A a2=2 ; > > 这是什么? > > 其实这就是**隐式类型转换**; > > **内置类型先转换成自定义类型**,然后构造一个A的**临时对象(临时对象具有常属性)**,临时对象再**拷贝构造a2** ,最后再调用**构造函数**,但是现在的编译器一般都会对这一过程进行优化,**它是直接构造**。 我们可以验证下: ![db36b704195d4c1eb3e5b829550f69f4.png][] 可以看到 vs2022 是进行了优化的,直接调用构造函数。 > 那么如果我们不想让这种隐式类型转换发生该怎么办呢? > > 只需再函数前面加上 **explicit 关键字**即可解决explicit > > ![b542b1dbef004190b05f4b0374e2988c.png][] > > 可以看到在加上这个关键字后,编译器就报错了。 -------------------- ## 三.static 成员 ## > ### 静态成员变量 ### > > 1.声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量; > > 注意:**静态成员变量一定要在类外进行初始化,且不加 static 关键字** > > 类静态成员即可用 **类名::静态成员 或者 对象.静态成员** 来访问; > > class A > { > public: > A(int b=1) > :_b(b) > {} > > private: > static int _a; //声明 > int _b; > }; > > int A::_a = 1; //定义 > ### 静态成员函数 ### > > 2.用static修饰的成员函数,称之为静态成员函数 > > 注意:**静态成员函数没有this指针,所以不能访问类里面的成员,也不能调用非静态成员 函数;** > **static 成员属于类,属于类的每个对象共享,存储在静态区**; > > 成员变量 -- 属于每个一个类对象,**存储在对象里面**; > > **静态成员也是类的成员**,受public、protected、private 访问限定符的限制。 -------------------- ## 四.友元 ## ### 友元函数 ### > **友元函数可以直接访问类的私有成员**,它是定义在类外部的普通函数,**不属于任何类,但需要在类的内部声明(友元函数并不受访问限定符限制)**,声明时需要加friend关键字。 例:**重载运算符 <<** 我们知道 cout<< 能自动识别内置类型,并打印;如果想要用这个打印自定义类型的话,就要重载一个,如果重载在类中的话,**那么它就属于类的成员函数了,第一个形参就是 this指针**,所以我们使用的时候只能这样写:**对象 << cout**,这样是不是很别扭,所以要想按照原来的写法,就不能把这个函数写在类的内部,只能写在外部,但我们有序要访问类里面的成员,这就在类内部声明友元函数了。 class Eve { friend ostream& operator<<(ostream& out, Eve& e); //友元函数声明 public: Eve(int a,int b) :_a(a) ,_b(b) {} private: int _a = 1; int _b = 2; }; ostream& operator<<(ostream& out, Eve& e) { out << e._a <<" "<<e._b << endl; return out; } int main() { Eve e(1,2); cout << e; return 0; } #### 总结 #### > 1.友元函数可访问类的私有和保护成员,但**不是类的成员函数**; > 2.友元函数**不能用const修饰**; > 3.友元函数可以在类定义的任何地方声明,**不受类访问限定符限制**; > 4.一个函数可以是多个类的友元函数; > 5.友元函数的调用与普通函数的调用原理相同; ### 友元类 ### > 友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。 > 1.**友元关系是单向的,不具有交换性**; > > 比如A类和B类,在A类中声明B类为其友元类,那么可以在A类中直接访问B类的私有成员 变量,但想在B类中访问A类中私有的成员变量则不行。 > > ![2867cf62f2954b7d97fae39086661980.png][] > 2.**友元关系不能传递**; > 3.如果C是B的友元, B是A的友元,则不能说明C时A的友元; > 4.**友元关系不能继承**,在继承位置再给大家详细介绍。 -------------------- ## 五.内部类 ## > 概念:**如果一个类定义在另一个类的内部,这个内部类就叫做内部类**。 > > 内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。**外部类对内部类没有任何优越的访问权限**。 > > 所以计算一个有内部类的类的大小时,只需要计算外部类的大小。 > 注意:**内部类天生是外部类的友元类**。 > 特性: > 1. 内部类可以定义在外部类的public、protected、private都是可以的。 > 2. **注意内部类可以直接访问外部类中的static成员**,不需要外部类的对象/类名。 > 3. **sizeof(外部类)=外部类,和内部类没有任何关系**。 class A { private: static int _a; int _b; class B { private: int _c; }; }; int main() { cout << sizeof(A) << endl; //会是多少呢? return 0; } 上述代码的结果是什么呢? 答案:4 因为只需要计算外部类的大小,**而静态成员变量是存储在静态区的,并不在类中**,所以只需计算成员变量 \_b 的大小,很明显是4。 -------------------- ## 六.匿名对象 ## 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; } -------------------- > **??本篇文章到此就结束了, 若有错误或是建议的话,欢迎小伙伴们指出;?️?** > > **??希望小伙伴们能支持支持博主啊,你们的支持对我很重要哦;??** > > **??谢谢你的阅读。??** ![78d3326eded845439ee33b1340c128f8.gif][] [26e580bde9144fe2a5d9b8690ac2614b.gif]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/22/770baac16dfc4fb5817d8fdbf9b5fef2.gif [1ac4d8876443439e9cda6b8c23908238.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/22/0c52f87fb5414416b0e958724e03f25f.png [a14dc58b786842ce9c0083e4dd5beeea.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/22/95b485a330d54947b0000bf9f1b8e7a5.png [Link 1]: https://mp.csdn.net/mp_blog/creation/editor/130458636 [eca7af87e2134b988b7b42bba7145630.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/22/53c6b74c9a6e4d668bf2671c02174da0.png [db36b704195d4c1eb3e5b829550f69f4.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/22/a8bd26da200b4be199ed19eac904b72e.png [b542b1dbef004190b05f4b0374e2988c.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/22/06449e83a6f34b509f4a5e11952ab71e.png [2867cf62f2954b7d97fae39086661980.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/22/c27a30a5e081456b98215611c7c452fd.png [78d3326eded845439ee33b1340c128f8.gif]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/22/9219533f546e47e9b7aeace19fb88a83.gif
相关 【C++初阶】类和对象修炼下 文章目录 一.四大默认成员函数 二.获取某年某月的天数 三.日期+=天数和日期+天数 四.日期-=天数和日期-天数 五.日期比较 六. 我不是女神ヾ/ 2024年04月03日 12:19/ 0 赞/ 35 阅读
相关 【C++初阶】类和对象修炼中 文章目录 一.构造函数 1.构造函数的简单认识 2.内置类型和自定义类型的处理方式 3.日期类和资源清理类 二.析构函 ゝ一纸荒年。/ 2024年04月03日 12:17/ 0 赞/ 33 阅读
相关 【C++初阶】类和对象修炼上 > 这一专题<类和对象>是C++学习者的第一道小坎,我将分为上中下三节给大家讲解 > > 1. 类和对象上:类和对象的引入,包括一些细碎的知识点,包括面向对象思想,类的实例 爱被打了一巴掌/ 2024年04月03日 11:33/ 0 赞/ 32 阅读
相关 【C++初阶】:类与对象(上) 类与对象 一.面向对象的初步认识 二.初步认识类 三.类的权限 四.类的声明和定义 五.封装 六.类的实例化 七.类的大小 柔情只为你懂/ 2024年03月23日 15:54/ 0 赞/ 7 阅读
相关 【C++初阶】:类与对象(下) 类与对象 一.再谈构造函数 1.初始化列表(构造函数的一部分) 2.explicit关键字 二.static成员 三.友元 爱被打了一巴掌/ 2024年03月22日 14:37/ 0 赞/ 7 阅读
相关 【C++初阶】:string类 string 一string的基本用法 二.迭代器 1.基本使用 2.语法糖 3.反向迭代器 4.con 缺乏、安全感/ 2024年03月17日 09:59/ 0 赞/ 13 阅读
相关 【C++】类和对象(下) 文章目录 一、再谈构造函数 1.1 构造函数体赋值 1.2 初始化列表 1.3 explicit关键字 二、stati 梦里梦外;/ 2023年10月14日 22:51/ 0 赞/ 91 阅读
相关 【C++ 初阶】类和对象 目录 一、面向过程vs面向对象 二、类的限定符及封装 三、类的实例化 四、this指针 五、默认成员函数 1. 构造函数 梦里梦外;/ 2022年09月16日 11:18/ 0 赞/ 153 阅读
还没有评论,来说两句吧...