C++之类与对象(完结撒花篇)

news/2024/9/17 18:39:14/ 标签: c++, 开发语言

目录

前言

1.再探构造函数

2.类型转换

3.static成员

4. 友元

5.内部类

6.匿名对象

7.对象拷贝时的编译器优化

结束语



前言

在前面的博客中,我们对类的默认成员函数都有了一定了解,同时实现了一个日期类对所学的没内容进行扩展延伸,本节我们将对类与对象进行大致的最终学习。

1.再探构造函数

• 之前实现构造函数时,初始化成员变量 主要使用函数体内赋值,构造函数初始化 还有一种方式,就是初始化列表,初始化列表的使用方式是以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
• 每个成员变量在初始化列表中 只能出现一次,语法理解上初始化列表可以认为是每个成员变量定义初始化的地方。
• 引用成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进行初始化,否则会编译报错。
#include <iostream>
using namespace std;
class Time {
public:Time(int hour=1): _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 x = 1;Date d1(x);d1.Print();return 0;
}

上述代码是修改后的正确代码展示

• C++11支持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显⽰在初始化列表初始化的成员使用的。
尽量使用初始化列表初始化,因为那些不在初始化列表初始化的成员也会走初始化列表,如果这个成员在声明位置给了缺省值,初始化列表会用这个缺省值初始化。如果你没有给缺省值,对于没有显示在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++并没有规定。对于没有显示在初始化列表初始化的自定义类型成员会调用这个成员类型的默认构造函数,如果没有默认构造会编译错误。
• 初始化列表中按照成员变量在类中声明顺序进行初始化,跟成员在初始化列表出现的的先后顺序无关。建议声明顺序和初始化列表顺序保持一致。
#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;
}
a9476e5b17574f9ca1d739ff35375ec8.png

 补充题目

下面程序的运行结果是什么(D)
A. 输出 1 1     B. 输出 2 2     C. 编译报错
D. 输出 1 随机值     E. 输出 1 2    F. 输出 2 1
#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();
}

b66642d82e22404f8ddeaf49c8e6d888.png

_a2_a1 的初始化顺序不符合它们在类中声明的顺序。这将导致 _a2 使用未初始化的 _a1 值,所以输出的_a2是个随机值

2.类型转换

C++ 支持内置类型(如 intfloat 等)隐式转换为类类型对象,只要类中定义了一个接受该内置类型作为参数的构造函数。这种构造函数通常称为单参数构造函数,能够允许编译器在需要时自动创建对象。

#include <iostream>
using namespace std;class MyClass {
public:// 单参数构造函数,接受一个 int 类型MyClass(int value) : _value(value) {cout << "MyClass constructed with value: " << _value << endl;}void Print() const {cout << "Value: " << _value << endl;}private:int _value;
};int main() {MyClass obj = 10; // 隐式转换,从 int 到 MyClassobj.Print(); // 输出: Value: 10MyClass anotherObj(20); // 显式构造anotherObj.Print(); // 输出: Value: 20return 0;
}

 5374931f75674d39abe366128c6f9c21.png

注意事项

  • 隐式转换的风险:

    • 尽管隐式转换很方便,但可能会导致代码的可读性降低,尤其是在较大的代码库中。为了避免不必要的隐式转换,可以将构造函数声明为 explicit,防止不小心的隐式转换:

class MyClass {

public:

explicit MyClass(int value)

: _value(value) {}

// ...

};

  • 多重构造:

    • 如果类中有多个构造函数,确保它们能够明确区分,以避免二义性的问题。

3.static成员

• 用 static修饰的成员变量,称之为静态成员变量,静态成员变量一定要在类外进行初始化。
• 静态成员变量为 所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区。
• 用static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针。
• 静态成员函数中可以访问其他的静态成员,但是不能访问非静态的,因为没有this指针。
• 非静态的成员函数,可以访问任意的静态成员变量和静态成员函数。
• 突破类域就可以访问静态成员,可以通过类名::静态成员 或者 对象.静态成员 来访问静态成员变量和静态成员函数。
• 静态成员也是类的成员,受public、protected、private 访问限定符的限制。
• 静态成员变量 不能在声明位置给缺省值初始化,因为缺省值是个构造函数初始化列表的,静态成员变量不属于某个对象,不走构造函数初始化列表。

#include <iostream>
using namespace std;
class A {
public:A() {++_count;}A(const A& count) {++_count;}~A() {--_count;}static int getcount() {return _count;}
private://类里面声明static int _count;
};
int A::_count = 520;
int main() {cout << A::getcount() << endl; // 输出:520A t1; // _count 增加到 521A t2(t1); // _count 增加到 522cout << A::getcount() << endl; // 输出:522// 此时 t1 和 t2 仍然存在cout << t1.getcount() << endl;//522cout << t2.getcount() << endl;//522{A t3(t1); // _count 增加到 523cout << A::getcount() << endl; // 输出:523} // t3 超出作用域, _count 减少到 522cout << A::getcount() << endl; // 输出:522return 0;
}

题目练习

设已经有A,B,C,D 4个类的定义,程序中A,B,C,D构造函数调⽤顺序为?(E)
设已经有A,B,C,D 4个类的定义,程序中A,B,C,D析构函数调⽤顺序为?(B)
C c;
int main() {
A a;
B b;
static D d;
return 0;
}
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

在全局或局部作用域中,构造函数的调用顺序如下:

  1. 全局和静态对象的构造:在程序启动时,全局对象(如果有)和静态对象会首先被构造。
  2. 局部对象的构造:然后,当程序进入 main() 函数时,局部对象的构造按定义顺序调用。

析构函数的调用顺序与构造函数的顺序相反。析构函数会在对象的生命周期结束时被调用,顺序如下:

  1. 局部对象的析构:当程序退出 main() 函数时,局部对象按定义的相反顺序析构。
  2. 全局和静态对象的析构:在 main() 函数结束后,全局对象和静态对象会被析构。

4. 友元

• 友元提供了一种突破类访问限定符封装的方式,友元分为: 友元函数和友元类,在函数声明或者类声明的前面 加friend,并且把友元声明放到一个类的里面。
• 外部友元函数可访问类的私有和保护成员,友元函数仅仅是一种声明,他不是类的成员函数。
• 友元函数可以在类定义的任何地方声明,不受类访问限定符限制。
一个函数可以是多个类的友元函数
友元类中的成员函数都可以是另一个类的友元函数,都可以访问另一个类中的私有和保护成员。
• 友元类的关系是单向的,不具有交换性,比如A类是B类的友元,但是B类不是A类的友元。
友元类关系不能传递,如果A是B的友元, B是C的友元,但是A不是B的友元。
• 有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。

#include <iostream>
using namespace std;class B;//前置声明
class A {friend void func(const A& a, const B& b);
private:int _a = 520;int _b = 1314;
};
class B {friend void func(const A& a, const B& b);
private:int _a = 1314;int _b = 520;
};
void func(const A& a, const B& b) {cout << a._a << endl;cout << b._b << endl;
}int main() {A a;B b;func(a, b);return 0;
}

a033ffeedee247a1af5d55fa60ccbace.png

#include<iostream>
using namespace std;
class A
{// 友元声明friend class B;
private:int _a1 = 520;int _a2 = 1314;
};
class B
{
public:void func1(const A& aa){cout << aa._a1 << endl;cout << _b2 << endl;}void func2(const A& aa){cout << aa._a2 << endl;cout << _b1 << endl;}
private:int _b1 = 520;int _b2 = 1314;
};
int main()
{A aa;B bb;bb.func1(aa);bb.func2(aa);return 0;
}

5.内部类

• 如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,跟定义在全局相比,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。
内部类默认是外部类的友元类
• 内部类本质也是一种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使用,那么可以考虑把A类设计为B的内部类, 如果放到private/protected位置,那么A类就是B类的专属内部类,其他地方都用不了。
#include <iostream>
using namespace std;class A {
private:static int _a; // 静态成员int _b;        // 非静态成员
public:class B {public:void print(const A& a) {cout << _a << endl;   // 访问静态成员cout << a._b << endl; // 访问非静态成员}};
};int A::_a = 520; // 静态成员初始化int main() {cout << "A类的大小:" << sizeof(A) << endl; // 输出 A 类的大小A::B b; // 创建 B 类的对象A aa;   // 创建 A 类的对象b.print(aa); // 调用 print 函数return 0;
}

24af069dc4504e63b7038df3585add7e.png

6.匿名对象

用类型(实参) 定义出来的对象叫做匿名对象,相比之前我们定义的 类型 对象名(实参) 定义出来的 叫有名对象
匿名对象生命周期只在当前一行,一般临时定义一个对象当前用一下即可,就可以定义匿名对象。
#include <iostream>
using namespace std;
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;}
};bool myfunction(int i, int j) { return (i > j); }int main()
{A aa1;  //有名对象// 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义//A aa2();// 生命周期只在当前一行A(); // 匿名对象A(1);Solution st;cout << st.Sum_Solution(10) << endl;// 为了更方便cout << Solution().Sum_Solution(10) << endl;return 0;
}

7e27414ec1e04d3489a22e383ed1adc0.png

7.对象拷贝时的编译器优化

• 现代编译器会为了尽可能提高程序的效率,在不影响正确性的情况下会尽可能减少一些传参和传参过程中可以省略的拷贝。
• 如何优化C++标准并没有严格规定,各个编译器会根据情况自行处理。当前主流的相对新一点的编译器对于连续一个表达式步骤中的连续拷贝会进行合并优化,有些更新更"激进"的编译还会进行跨行跨表达式的合并优化。
#include <iostream>
using namespace std;
class A {
public:A(int a=0):_a(a){cout << "A(int a)" << endl;}A(const A& aa) :_a(aa._a){cout << "A(const A& aa) " << endl;}A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a = aa._a;}return *this;}~A() {cout << "~A()" << endl;}void Print(){cout << "A::Print->" << _a << endl;}A& operator++(){_a += 100;return *this;}
private:int _a ;
};
void f1(A aa)
{}
A f2()
{A aa(1);++aa;cout << "##########" << endl;return aa;
}
int main() {A aa1 ;aa1.Print();//const A& aa2 = 2;//A aa3(aa2);//A aa1;//f1(aa1);//cout << endl;// 隐式类型,连续构造+拷⻉构造->优化为直接构造//f1(1);// ⼀个表达式中,连续构造+拷⻉构造->优化为⼀个构造//f1(A(2));//cout << endl;//cout << "***********************************************" << endl;// 传值返回// 返回时⼀个表达式中,连续拷⻉构造+拷⻉构造->优化⼀个拷⻉构造 (vs2019)// ⼀些编译器会优化得更厉害,进⾏跨⾏合并优化,直接变为构造。(vs2022)//f2();//cout << endl;// 返回时⼀个表达式中,连续拷⻉构造+拷⻉构造->优化⼀个拷⻉构造 (vs2019)// ⼀些编译器会优化得更厉害,进⾏跨⾏合并优化,直接变为构造。(vs2022)// A aa2 = f2();// cout << endl;// ⼀个表达式中,连续拷⻉构造+赋值重载->⽆法优化aa1 = f2();cout << endl;A ret = f2();ret.Print();cout << "*********" << endl << endl;//return 0;}

结束语

本节内容到此结束,类与对象的学习也暂时告别一段落了,希望接下来继续能和大家探讨C++的学习,最后呢,感谢各位友友的支持,讲解不足之处也希望大家多多包涵!!! 


http://www.ppmy.cn/news/1507300.html

相关文章

【C++ 面试 - 基础题】每日 3 题(十一)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…

计算机网络TCP/UDP知识点

这是一些在学习过程中关于计算机网络八股文的一些知识点记录&#xff1a; TCP/UDP TCP怎么保证可靠性 1.序列号&#xff0c;确认应答&#xff0c;超时重传 数据到达接收方&#xff0c;接收方需要发出一个确认应答&#xff0c;表示已经收到该数据段&#xff0c;并且确认序号…

【HarmonyOS NEXT星河版开发学习】小型测试案例06-小红书卡片

个人主页→VON 收录专栏→鸿蒙开发小型案例总结​​​​​ 基础语法部分会发布于github 和 gitee上面&#xff08;暂未发布&#xff09; 前言 在鸿蒙&#xff08;HarmonyOS&#xff09;开发中&#xff0c;自适应伸缩是指应用程序能够根据不同设备的屏幕尺寸、分辨率和形态&…

Leetcode每日刷题之 11. 盛最多水的容器(C++)

1. 题目解析 根据题目我们知道本题我们需要由给出的数组找出所有容器中盛水最多的一个&#xff0c;即核心就是先求出所有容器后遍历找出最大的即可 2. 算法原理 本题使用到的算法是双指针&#xff0c;在使用暴力解法遍历所有容器的时候会出现超时的问题&#xff0c;而是用双指针…

【机器学习数据预处理】特征工程

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈Python机器学习 ⌋ ⌋ ⌋ 机器学习是一门人工智能的分支学科&#xff0c;通过算法和模型让计算机从数据中学习&#xff0c;进行模型训练和优化&#xff0c;做出预测、分类和决策支持。Python成为机器学习的首选语言&#xff0c;…

mysql本地3306通过nginx映射到外网

mysql本地3306通过nginx映射到外网 安装nginx, 版本大于1.19x yum install nginx 安装stream-module模块 查看yum源&#xff0c;查找有没有 nginx-mod-stream.x86_64 yum list | grep nginx 安装stream, 安装后就可以使用stream这个功能 yum install -y nginx-mod-stream.x8…

LVS详解

一、概念简述 1.1LVS概念简述 1.1.1 LVS LVS&#xff08;Linux Virtual Server&#xff09;即Linux虚拟服务器&#xff0c;是由章文嵩博士主导的开源负载均衡项目&#xff0c;目前LVS已经被集成到Linux内核模块中。LVS基于内核网络层工作&#xff0c;有着超强的并发处理能力&…

av.codec.codec.UnknownCodecError: libx264

遇到 av.codec.codec.UnknownCodecError: libx264 这个错误通常意味着 PyAV 库尝试使用 libx264 编码器来编码或解码视频&#xff0c;但该编码器在你的系统中不可用。 libx264 是一个广泛使用的 H.264 视频编码库。如果你正在使用 PyAV 来处理视频&#xff0c;特别是当你尝试读…

CA证书和openssl介绍

文章目录 一、加密和算法常见的安全攻击加密算法和协议对称加密非对称加密算法 二、CA和证书中间人攻击CA和证书安全协议SSL/TLS协议介绍HTTPS 三、opensslopenssl介绍使用openssl实现对称加密使用openssl命令生成加密密码生成随机密码建立私有CA证书申请颁发建立私有CA实际例子…

C++初阶_1:缺省参数

闲话少叙&#xff0c;咱们这章来说说缺省参数。 何为缺省参数&#xff1f; 先请诸位来看一段代码&#xff1a; //缺省参数 #include <iostream> using namespace std; void Fun(int a 10)//此处为函数的定义&#xff0c;这里形参a给了一个缺省值10 {cout << a …

测试日记day3

1、写出5条mysql常用命令 答&#xff1a; show databases&#xff1a;用于显示当前 MySQL 服务器中的所有数据库。create database [database_name]&#xff1a;用于创建新的数据库。use [database_name]&#xff1a;用户选择要使用的数据库。show tables&#xff1a;在已经选…

机器学习/深度学习——模型的欠拟合和过拟合,正则化方法详解

机器学习/深度学习——模型的欠拟合和过拟合&#xff0c;正则化方法 详解 搭配以下文章进行学习&#xff1a; 卷积神经网络&#xff1a; 深度学习——卷积神经网络&#xff08;convolutional neural network&#xff09;CNN详解&#xff08;一&#xff09;——概述. 步骤清晰…

在 FastAPI 项目中使用 Python 注解类型实现通用返回结构

前言 在开发 API 时&#xff0c;确保一致的响应结构是一个良好的实践。无论是前端开发人员还是后端开发人员&#xff0c;都希望能够依赖一个统一的返回格式&#xff0c;这样可以简化调试和处理逻辑。在这篇文章中&#xff0c;我们将探讨如何在 FastAPI 项目中使用 Python 的注…

IDE连接本地docker(基于windows的docker desktop)

1.在docker desktop中的setting中&#xff0c;打开 “Expose daemon on tcp://localhost:2375 without TLS Exposing daemon on TCP without TLS helps legacy clients connect to the daemon. It also makes yourself vulnerable to remote code execution attacks. Use wi…

Geoscene Pro的三维

一、场景设置 1.3D视图分为全局场景和局部场景。在Geoscene Pro中&#xff0c;两个场景可以自由切换。 &#xff08;1&#xff09;全局场景有固定的坐标系GCS&#xff08;WGS84、CGCS2000&#xff09;&#xff0c;并在全球比例尺下展示&#xff08;全球范围&#xff09;。可以…

Linux云计算 |【第二阶段】CLUSTER-DAY3

主要内容&#xff1a; Ceph概述、部署Ceph集群、Ceph块存储 一、分布式存储基础 分布式系统&#xff08;Distributed File System&#xff09;是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统。文件系统管理的物理存储资源不一定直接连接在本…

基于QT实现的简易WPS(已开源)

一、开发工具及开源地址&#xff1a; 开发工具&#xff1a;QTCreator &#xff0c;QT 5 开源地址&#xff1a; GitHub - Whale-xh/WPS_official: Simple WPS based on QTSimple WPS based on QT. Contribute to Whale-xh/WPS_official development by creating an acc…

【Linux学习】动静态库从原理到制作

&#x1f351;个人主页&#xff1a;Jupiter. &#x1f680; 所属专栏&#xff1a;Linux从入门到进阶 欢迎大家点赞收藏评论&#x1f60a; 目录 &#x1f351;动静态库&#x1f41f;动静态库的制作与使用&#x1f680;生成静态库&#x1f512;生成动态库 &#x1f98c;动态库的查…

C# winform 三层架构增删改查,(删除篇)

一.留言 C# wnform 三层架构增删改查&#xff0c;本篇是增删改查是删除篇&#xff0c;也就增删改查外加一个登录更新完&#xff0c;后续考虑出一个增删改查就是不用三层架构&#xff0c;在uI里面 直接写完&#xff0c;并且放一个帮助类&#xff0c;基本十分钟可以写完一套增删…

Vue3 的福音框架:Arco Design

前言 随着前端技术的发展&#xff0c;Vue3 作为现代 JavaScript 框架的佼佼者&#xff0c;凭借其优雅的设计和卓越的性能赢得了众多开发者的喜爱。然而&#xff0c;仅仅拥有一个强大的框架是不够的&#xff0c;选择一个合适的 UI 组件库同样至关重要。在众多的 Vue UI 组件库中…