C++基础——类与对象
1 概述
C++是面向对象的语言,面向对象语言三大特性:封装、继承、多态。
C++将万事万物抽象为对象,对象上有其属性和行为。
2 封装
2.1 封装的意义
封装是面向对象的三大特性之一,封装将属性和行为作为一个整体,对属性和行为加以权限控制。
创建类语法:
class 类名 {
访问权限:属性;行为;
};
示例:
class Student {
private:string m_name;int m_age;
public:Student(string name, int age) {m_name = name;m_age = age;}string getName() {return m_name;}
};
上面除了定义属性和行为,也定义了权限访问控制符
C++包含三种访问控制符:
- public 可被类中函数、子类函数、友元函数和类对象访问
- protected 可被类中函数、子类函数、友元函数访问
- private 可被类中函数和友元函数访问
2.2 struct和class区别
二者的区别在于默认的访问权限不同,struct默认访问权限为public,class默认访问权限为private
#include <iostream>
using namespace std;class C1 {int m_A;
};struct S1 {int m_A;
};int main() {C1 c1;//c1.m_A; 报错,无法访问S1 s1;s1.m_A = 10;return 0;
}
2.3 成员属性设置为私有
通常,将成员属性设置为私有,并提供设置和获取的方法。
这样的好处是可以控制成员属性的访问权限,对于写权限,能够检测数据的有效性。
class Test {
public://num1提供读写接口void setNum1(int num1) {m_num1 = num1;}int getNum1() {return m_num1;}//num2只提供读接口int getNum2() {return m_num2;}//num3只提供写接口,并判定数据范围void setNum3(int num3) {if (num3 > 0) {m_num3 = num3;} else {m_num3 = 0;}}private:int m_num1;int m_num2;int m_num3;
};
封装的思想通常会将属性设置为私有,暴露必要的修改行为给外部。
3 对象创建和清理
3.1 构造函数和析构函数
一个类具有最基础的两个函数是构造函数和析构函数,即使程序员未添加这两个函数,编译器也会生成一个默认的版本。
其中构造函数用于为类创建一个对象,并进行一些初始化的工作。
析构函数与构造函数相反,是为了清理一个对象,并释放资源。
构造函数和析构函数都是由编译器自动调用。
class Test {
private:int m_num;
public:Test();Test(int num) {m_num = num;}~Test();
};
构造函数可以重载,而析构函数只能有一个。构造函数可以通过传入不同的参数来重载,而析构函数没有参数。
当未定义构造函数和析构函数时,编译器会自动生成以下两个实现:
Test(){}
~Test(){}
默认会生成两个空实现。
而如果提供了有参构造函数,则编译器不会提供默认的空参构造函数,如果有此使用场景,则需要自己再提供一个空参构造函数。
3.2 构造函数分类及调用方式
构造函数有两种分类方式:
按参数分为:有参构造和无参构造
按类型分类:普通构造和拷贝构造
三种调用方式:
括号法
显示法
隐藏转换法
#include <iostream>using namespace std;class Person {
public://无参构造函数Person() {cout << "无参构造函数" << endl;}//有参构造函数Person(int age) {cout << "有参构造函数" << endl;m_age = age;}//拷贝构造函数Person(const Person &person) {cout << "拷贝构造函数" << endl;m_age = person.m_age;}~Person() {cout << "析构函数" << endl;}public:int m_age;
};void test1() {//调用无参构造函数Person p1;//显示调用无参构造函数Person p2 = Person();//调用拷贝构造函数Person P3(p1);
}void test2() {//括号法Person p1(10);//括号不能用于调用无参构造函数,这样定义会被认为是一个函数声明//Person p();//显示法Person p2 = Person(10);Person p3 = Person(p2);//创建匿名对象,无法使用,直接析构Person();Person(10);//隐式转换法,隐式调用有参构造函数和拷贝构造函数Person p4 = 10;Person p5 = p4;//不能利用拷贝构造初始化匿名对象,会被认为是对象声明//Person p6(p4);
}int main() {test1();test2();
}
输出:
无参构造函数
无参构造函数
拷贝构造函数
析构函数
析构函数
析构函数
有参构造函数
有参构造函数
拷贝构造函数
无参构造函数
析构函数
有参构造函数
析构函数
有参构造函数
拷贝构造函数
析构函数
析构函数
析构函数
析构函数
析构函数
比较需要注意的是隐藏转换法
3.3 拷贝构造函数的调用时机
调用拷贝构造函数有三种情况:
- 使用一个已有的对象去初始化另一个对象
- 值传递的方式给对象类型参数传值
- 以值的方式返回局部对象
#include <iostream>using namespace std;class Person {
public://无参构造函数Person() {cout << "无参构造函数" << endl;}//有参构造函数Person(int age) {cout << "有参构造函数" << endl;m_age = age;}//拷贝构造函数Person(const Person &person) {cout << "拷贝构造函数" << endl;m_age = person.m_age;}~Person() {cout << "析构函数" << endl;}public:int m_age;
};void test1() {//调用有参构造函数Person p1(10);//括号法调用拷贝构造函数Person p2(p1);//隐式转换法调用拷贝构造函数Person p3 = p1;//显示调用拷贝构造函数Person p4 = Person(p1);//赋值操作,不会调用拷贝构造函数Person p5;p5 = p1;
}void doSomething1(Person p){}
void test2() {Person p;//对象赋值给形参,调用拷贝构造函数doSomething1(p);
}Person doSomething2() {Person p;return p;
}
void test3() {Person p = doSomething2();
}int main() {test1();test2();test3();
}
输出:
有参构造函数
拷贝构造函数
拷贝构造函数
拷贝构造函数
无参构造函数
析构函数
析构函数
析构函数
析构函数
析构函数
无参构造函数
拷贝构造函数
析构函数
析构函数
无参构造函数
析构函数
最后返回局部对象与预想中有差异是因为编译器优化,感兴趣可以搜索RVO了解。
由于返回值和值传递传对象都会导致对象复制,对象大小会随着属性的增加而增加,所以一般对象作为参数的时候,都是使用引用或者指针进行传参或返回。
3.4 构造函数调用规则
默认情况下,C++编译器会至少给一个类添加3个函数:
- 默认构造函数(无参构造函数,实现为空)
- 默认析构函数(空实现析构函数)
- 默认拷贝构造函数(对定义的属性进行拷贝)
构造函数生成规则如下:
- 如果用户定义有参构造函数,则编译器不会提供默认无参构造函数,但还是会提供默认拷贝构造函数
- 如果用户定义拷贝构造函数,则编译器不会提供默认无参构造函数
1、使用编译器默认函数
class Person {
public:int m_age;
};int main() {Person p1;p1.m_age = 10;Person p2 = p1;cout << "p2.m_age = " << p2.m_age << endl;
}
这里调用了默认无参构造和拷贝构造函数。析构函数由于没有实现所以无法体现。
2、提供有参构造的情况
class Person {
public://无参构造函数//Person() {// cout << "无参构造函数" << endl;//}//有参构造函数Person(int age) {cout << "有参构造函数" << endl;m_age = age;}//拷贝构造函数Person(const Person &person) {cout << "拷贝构造函数" << endl;m_age = person.m_age;}~Person() {cout << "析构函数" << endl;}public:int m_age;
};
没有匹配的构造函数调用,编译器不提供默认构造函数。
3、提供拷贝构造函数
class Person {
public://拷贝构造函数Person(const Person &person) {cout << "拷贝构造函数" << endl;m_age = person.m_age;}~Person() {cout << "析构函数" << endl;}public:int m_age;
};
提供了拷贝构造函数后,不会提供默认无参构造函数。
3.5 深拷贝和浅拷贝
浅拷贝:简单的赋值拷贝,指向同一个内存区域
深拷贝:在堆中重新申请空间,进行拷贝
class Person {
public://无参(默认)构造函数Person() {cout << "无参构造函数!" << endl;}//有参构造函数Person(int age ,int height) {cout << "有参构造函数!" << endl;m_age = age;m_height = new int(height);}//拷贝构造函数 Person(const Person& p) {cout << "拷贝构造函数!" << endl;//如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题m_age = p.m_age;m_height = new int(*p.m_height);}//析构函数~Person() {cout << "析构函数!" << endl;if (m_height != NULL){delete m_height;}}
public:int m_age;int* m_height;
};void test01()
{Person p1(18, 180);Person p2(p1);cout << "p1的年龄: " << p1.m_age << " 身高: " << *p1.m_height << endl;cout << "p2的年龄: " << p2.m_age << " 身高: " << *p2.m_height << endl;
}int main() {test01();return 0;
}
由于类中的属性是在堆上分配的,如果使用浅拷贝,会导致两个对象的m_height都指向了同一个堆内存区域,而当一个对象析构之后,会释放该内存区域,而另一个对象还在使用该内存区域,会导致不确定的结果。而第二个对象析构时,则会导致重复释放堆区,会导致程序终止。所以如果有堆上分配内存的属性,需要使用深拷贝,重新申请堆内存区域进行赋值。
示例如下:
void test1() {Person p1(18, 180);{Person p2(p1);cout << "p2.height = " << *p2.m_height << endl;}cout << "p1.height = " << *p1.m_height << endl;
}int main() {test1();
}
输出:
有参构造函数
p2.height = 180
析构函数
p1.height = 887973408
析构函数Process finished with exit code -1073740940 (0xC0000374)
上面是注释了深拷贝的结果,p2正常访问,然后p2被析构,此时p1的m_height指向的内存实际已经释放,所以访问该内存会导致不确定的结果,然后p1会重复释放m_height指向的内存,导致程序出错终止。
3.6 初始化列表
C++提供了一种初始化列表的语法来初始化属性
class Person {
public:传统方式初始化//Person(int a, int b, int c) {// m_A = a;// m_B = b;// m_C = c;//}//初始化列表方式初始化Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) {}void PrintPerson() {cout << "mA:" << m_A << endl;cout << "mB:" << m_B << endl;cout << "mC:" << m_C << endl;}
private:int m_A;int m_B;int m_C;
};int main() {Person p(1, 2, 3);p.PrintPerson();return 0;
}
初始化列表的语法可以省略原始构造函数中的一些模版代码。
3.7 类对象作为类成员
class Phone
{
public:Phone(string name){m_PhoneName = name;cout << "Phone构造" << endl;}~Phone(){cout << "Phone析构" << endl;}string m_PhoneName;
};class Person
{
public://初始化列表可以告诉编译器调用哪一个构造函数Person(string name, string pName) :m_Name(name), m_Phone(pName){cout << "Person构造" << endl;}~Person(){cout << "Person析构" << endl;}void playGame(){cout << m_Name << " 使用" << m_Phone.m_PhoneName << " 牌手机! " << endl;}string m_Name;Phone m_Phone;};
void test01()
{//当类中成员是其他类对象时,我们称该成员为 对象成员//构造的顺序是 :先调用对象成员的构造,再调用本类构造//析构顺序与构造相反Person p("张三" , "苹果X");p.playGame();}int main() {test01();
}
先调用对象成员的构造函数,然后调用本类的构造函数。析构的时候正好与构造的时候顺序相反。
3.8 静态成员
静态成员就是在成员变量或者成员函数前加上static关键字
- 静态成员变量
- 所有对象共享同一份数据
- 在编译阶段分配内存
- 类内声明,类外初始化
- 静态成员函数
- 所有对象共享同一个函数
- 静态成员函数只能访问静态成员变量
1、静态成员变量
class Person
{public:static int m_A; //静态成员变量
private:static int m_B; //静态成员变量也是有访问权限的
};
//类外初始化
int Person::m_A = 10;
int Person::m_B = 10;void test01()
{//静态成员变量两种访问方式//1、通过对象Person p1;p1.m_A = 100;cout << "p1.m_A = " << p1.m_A << endl;Person p2;p2.m_A = 200;cout << "p1.m_A = " << p1.m_A << endl; //共享同一份数据cout << "p2.m_A = " << p2.m_A << endl;//2、通过类名cout << "m_A = " << Person::m_A << endl;//cout << "m_B = " << Person::m_B << endl; //私有权限访问不到
}int main() {test01();
}
静态成员变量也有访问权限,与非静态成员变量的访问权限一样。静态成员变量可以通过对象和类名访问。
class Person
{
public:static void func(){cout << "func调用" << endl;m_A = 100;//m_B = 100; //错误,不可以访问非静态成员变量}static int m_A; //静态成员变量int m_B; //
private://静态成员函数也是有访问权限的static void func2(){cout << "func2调用" << endl;}
};
int Person::m_A = 10;void test01()
{//静态成员变量两种访问方式//1、通过对象Person p1;p1.func();//2、通过类名Person::func();//Person::func2(); //私有权限访问不到
}int main() {test01();
}
静态成员函数只能访问静态成员变量,也只能直接调用静态成员函数,可以在其中创建对象来调用非静态成员函数,因为非静态成员函数属于对象。静态成员函数也可以通过对象和类名两种方式调用。
相关文章:

C++基础——类与对象
1 概述 C是面向对象的语言,面向对象语言三大特性:封装、继承、多态。 C将万事万物抽象为对象,对象上有其属性和行为。 2 封装 2.1 封装的意义 封装是面向对象的三大特性之一,封装将属性和行为作为一个整体,对属性和…...

人工智能-卷积神经网络
从全连接层到卷积 我们之前讨论的多层感知机十分适合处理表格数据,其中行对应样本,列对应特征。 对于表格数据,我们寻找的模式可能涉及特征之间的交互,但是我们不能预先假设任何与特征交互相关的先验结构。 此时,多层感…...

MySQL的event的使用方法
MySQL的event的使用方法 一、事件定时策略 1、查看event事件开启状态 SHOW VARIABLES LIKE event_scheduler;如图,Value值 ON:打开,OFF:关闭。 2、设置event事件打开 SET GLOBAL event_scheduler ON;如果MySQL重启了&#x…...

Leetcode Daily Challenge 1845. Seat Reservation Manager
1845. Seat Reservation Manager 题目要求:初始化一个SeatManager类包括默认构造函数和类函数,所有的seat初始化为true。reverse函数返回最小的true,然后把这个编号的椅子赋值为false。unreverse(seatNumber)函数把编号为seatNumber的椅子恢…...

Blender vs 3ds Max:谁才是3D软件的未来
在不断发展的3D建模和动画领域,两大软件巨头Blender和3ds Max一直在争夺顶级地位。 随着技术的进步和用户需求的演变,一个重要问题逐渐浮出水面:Blender是否最终会取代3ds Max?本文将深入探讨二者各自的优势和劣势、当前状况&…...

MapReduce:大数据处理的范式
一、介绍 在当今的数字时代,生成和收集的数据量正以前所未有的速度增长。这种数据的爆炸式增长催生了大数据领域,传统的数据处理方法往往不足。MapReduce是一个编程模型和相关框架,已成为应对大数据处理挑战的强大解决方案。本文探讨了MapRed…...

【已解决】ModuleNotFoundError: No module named ‘dgl‘
禁止使用下面方法安装DGL,这种方法会更新你的pytorch版本,环境越变越乱 pip install dgl 二是进入DGL官网:Deep Graph Library (dgl.ai),了解自己的配置情况,比如我cuda11.8,ubuntu,当然和linux是一样的 …...

R 复习 菜鸟教程
R语言老师说R好就业,学就完了 基础语法 cat()可以拼接函数: > cat(1, "加", 1, "等于", 2, \n) 1 加 1 等于 2sink():重定向 sink("r_test.txt", splitTRUE) # 控制台同样输出 for (i in 1:5) print(i…...

第十二章《搞懂算法:朴素贝叶斯是怎么回事》笔记
朴素贝叶斯是经典的机器学习算法,也是统计模型中的一个基本方法。它的基本思想是利用统计学中的条件概率来进行分类。它是一种有监督学习算法,其中“朴素”是指该算法基于样本特征之间相互独立这个“朴素”假设。朴素贝叶斯原理简单、容易实现࿰…...

【从0到1开发一个网关】网关Mock功能的实现
文章目录 什么是Mock?如何实现Mock什么是Mock? Mock(模拟)是一种测试技术,用于创建虚拟对象来模拟真实对象的行为。Mock对象模拟了真实对象的行为,但是不依赖于真实对象的实现细节。它们可以在测试中替代真实对象,以便进行独立的单元测试。 需要使用Mock的原因包括以下几…...

前端框架Vue学习 ——(三)Vue生命周期
生命周期:指一个对象从创建到销毁的整个过程。 生命周期的八个阶段:每触发一个生命周期事件,会自动执行一个生命周期方法(钩子) mounted:挂载完成,Vue 初始化成功,HTML 页面渲染成功…...

相机滤镜软件Nevercenter CameraBag Photo mac中文版特点介绍
Nevercenter CameraBag Photo mac是一款相机和滤镜应用程序,它提供了一系列先进的滤镜、调整工具和预设,可以帮助用户快速地优化和编辑照片。 Nevercenter CameraBag Photo mac软件特点介绍 1. 滤镜:Nevercenter CameraBag Photo提供了超过2…...

游戏专用....
游戏专用:星际战甲 APP窗口以及键鼠监控 import tkinter as tk import time,threading from pynput.keyboard import Key,Listener import pynput.keyboard as kbclass myClass:def __init__(self):self.root tk.Tk()self.new_text self.flag threading.Event()…...

第三方登录和第三方支付
第三方登录 在现代Web应用中,提供第三方登录选项已经变得非常普遍。用户可以使用其社交媒体或其他在线帐户(如Google、GitHub或Facebook)来访问您的应用程序,而无需创建新的用户名和密码。这提供了更好的用户体验,减少…...

SpringMvc执行流程(含过滤器Filter+拦截器interceptor)
目录 1.Mvc的概念 2.SpringMvc的概念 3.SpringMvc的核心组件 4.SpringMvc的执行流程 5.SpringMvcFilterInterceptor执行流程 一、Mvc的概念 Mvc(Model View Controller):Mvc是一种设计规范,它将数据、视图、业务逻辑代码进行分离,降低代码…...

【UDS基础】简单介绍“统一诊断服务“
1. 前言 我们将在这个实用教程中介绍UDS的基础知识,重点关注在CAN总线上的UDS(UDSonCAN)和CAN诊断(DoCAN)。此外,我们还会介绍ISO-TP协议,并解释UDS、OBD2、WWH-OBD和OBDonUDS之间的差异。 最后,我们将解释如何请求、记录和解码UDS消息,并提供一些实际示例,例如记录…...

深度学习框架TensorFlow.NET之数据类型及张量2(C#)
环境搭建参考: 深度学习框架TensorFlow.NET环境搭建1(C#)-CSDN博客 由于本文作者水平有限,如有写得不对的地方,往指出 声明变量:tf.Variable 声明常量:tf.constant 下面通过代码的方式进行学…...

Pandas指定多列组合形成新列
目录 1、数据准备2、多列组合 1、数据准备 df pd.DataFrame({first_name: [A, B], last_name: [a, b]}) print(df.to_string()) first_name last_name 0 A a 1 B b 2、多列组合 2.1、方式一:使用cat() df[full_name] df[firs…...

硕鼠——视频下载利器
相信很多做自媒体、剪辑的同志们,经常会遇到一个棘手的问题:剪辑的素材从何而来。诸如很多高燃混剪的视频,往往需要多个影视作品中的原画来进行二次创作,可是这些视频素材从何而来呢? 有小伙伴们提出,通过录…...

Android 13.0 Launcher3 app图标长按去掉应用信息按钮
1.前言 在13.0的rom定制化开发中,在Launcher3定制化开发中,对Launcher3的定制化功能中,在Launcher3的app列表页会在长按时,弹出微件和应用信息两个按钮,点击对应的按钮跳转到相关的功能页面, 现在由于产品需求要求禁用应用信息,不让进入到应用信息页面所以要去掉应用信息…...

10 DETR 论文精读【论文精读】End-to-End Object Detection with Transformers
目录 DETR 这篇论文,大家为什么喜欢它?为什么大家说它是一个目标检测里的里程碑式的工作?而且为什么说它是一个全新的架构? 1 题目 2摘要 2.1新的任务定义:把这个目标检测这个任务直接看成是一个集合预测的问题 2.…...

高数笔记05:不定积分与定积分
图源:文心一言 时间比较紧张,仅导图~~🥝🥝 第1版:查资料、画导图~🧩🧩 参考资料:《高等数学 基础篇》武忠祥 🐳目录 🐳目录 🐳不定积分 &#…...

【代码随想录】算法训练计划13
1、347. 前 K 个高频元素 题目: 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 输入: nums [1,1,1,2,2,3], k 2 输出: [1,2] 思路: sort.Slice学习一下,其实还有so…...

Python图像处理之OpenCV模块
Python图像处理 1、OpenCV模块简介2、OpenCV模块图像常用操作3、PIL与OpenCV图像格式转换4、图像识别应用案例4.1、人脸识别4.2、车牌识别4.3、文本识别1、OpenCV模块简介 OpenCV(Open Source Computer Vision Library)是一个基于BSD许可(开源)发行的跨平台计算机视觉库,主…...

动态规划-丑数
** 描述 把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第 n个丑数。 数据范围: 0≤n≤2000 要求&#x…...

【MogDB/openGauss的三种函数稳定性关键字】
一、ORACLE中的类似的函数稳定性关键字(DETERMINISTIC) 在ORACLE里,function有着一个DETERMINISTIC参数,它表示一个函数在输入不变的情况下输出是否确定,只要输入的参数一样,返回的结果一定一样的…...

java-对Integer.MAX_VALUE做加法
public static void main(String[] args) {int maxValue Integer.MAX_VALUE;System.out.println("maxValue1 " (maxValue1));System.out.println("maxValue2 " (maxValue2));System.out.println("maxValue3 " (maxValue3));}//结果 maxVa…...

【学习笔记】[COCI2018-2019#1] Teoretičar
首先,可以发现 C C C等于所有点度数的最大值,我们能用到的颜色数目为 2 x ≥ C 2^x\ge C 2x≥C。 考虑分治,将边集划分为 E E 1 E 2 EE_1E_2 EE1E2,使得 E 1 , E 2 E_1,E_2 E1,E2中点度数的最大值都不超过 2 x − 1 2^…...

64位Office API声明语句第112讲
跟我学VBA,我这里专注VBA, 授人以渔。我98年开始,从源码接触VBA已经20余年了,随着年龄的增长,越来越觉得有必要把这项技能传递给需要这项技术的职场人员。希望职场和数据打交道的朋友,都来学习VBA,利用VBA,起码可以提高…...

C++ day3作业
1> 思维导图 2> 自己封装一个矩形类(Rect),拥有私有属性:宽度(width)、高度(height), 定义公有成员函数: 初始化函数:void init(int w, int h) 更改宽度的函数:set_w(int w) 更改高度的函数:set_h(int h) 输出该矩形的周长和面积函数:void s…...