类和对象学习笔记
类和对象
- 类的定义
- this指针
- 类的6个默认成员函数
- 构造函数
- 析构函数
- 拷贝构造函数
- 赋值运算符重载
- 赋值运算符重载
- 运算符重载
- const成员
- 取地址操作符重载
- const取地址操作符重载
- 初始化列表
- explicit关键字
- static成员
- 匿名对象
- 友元
- 内部类
- 拷贝对象时编译器的优化
类的定义
c++类的定义形式为:
class className
{
pubilic://...
private://...
};
在类的内部可以定义变量和函数,c++可以通过3个访问限定符来限制类的成员的访问权限:
pubilc:在该作用域的成员在类外可以直接访问
private和protected:在该作用域内的成员在类外不能直接访问
c++为了兼容c,就将c的结构体提升成了类,使用struct和class定义类的唯一区别是:class的成员的默认访问权限为private,struct的成员的默认访问权限是public,其他的并无区别。
在类里面定义的成员函数,编译器会将其当成内联函数来处理,但是否展开,最终还是取决于编译器。我们建议将短小的函数直接在类里面定义,其他的函数则声明和定义分离。
对象是类类型的实例化,类类型是对实例化对象的描述,对于一个类实例化出的多个对象来说,除了成员变量用来存储不一样的数据,成员函数都是一样的,因此为了节省空间,对象只保存成员变量,而成员函数则放在公共的代码段。由此,一个类的大小只需计算成员变量的大小即可,其计算方法与计算结构体的大小的方法一致结构体大小的计算。对于空类,编译器为了标识该类的存在,给了其一个字节的大小。
this指针
c++编译器给每一个非静态成员函数增加了一个隐藏的指针参数,该指针指向当前对象,当对象调用成员函数时就可以通过该指针访问该对象的成员变量,该指针由编译器自行传递,不需要用户来完成,用户可以在类里面显示使用this指针。
//定义了一个类
class Student
{
pubilc:void Print()//这里有一个默认this指针,相当于void Print(Student *this){cout<<_name<<_age<<_sex;//cout<<this->_age,显示使用this指针}
private:_name[20]="zhangsan";_age=20;_sex[7]="male"
};int main()
{Student s;//调用打印函数打印学生信息s.Print();//相当于s.Print(&s)return 0;
}
this指针特性:
1.this指针的类型为:类类型*const(如Student *const this),因此不能给this指针赋值。
2.this指针只能在成员函数内部使用。
3.this指针本质上是成员函数的形参,所以对象中不存储this指针(同普通函数参数一样,存放在栈区,VS存放在寄存器)。
4.this指针是成员函数参数列表隐藏着的第一个参数。
5.this指针可以为空。
class Student
{
pubilc:void Print(){cout<<“c++”;}
private:_name[20]="zhangsan";_age=20;_sex[7]="male"
};int main()
{Student* s=nullptr;s->Print();//由于成员函数不在对象中,此处不需要解引用,故代码可以正常执行//此时this指针为空return 0;
}
class Student
{
pubilc:void Print(){cout<<_age;}
private:_name[20]="zhangsan";_age=20;_sex[7]="male"
};int main()
{Student*s=nullptr;s->Print();//该代码编译通过,但运行崩溃return 0;
}
类的6个默认成员函数
默认成员函数是当用户没有显式定义时,编译器自动生成的函数。
构造函数
构造函数是名字与类名相同的函数,创建对象时由编译器自动调用,用于给数据成员一个初始值,即初始化对象,该函数在对象整个生命周期内只调用一次。同时构造函数无返回值,可以重载,在调用无参的构造函数时后面不需要跟括号。
class Student
{
pubilc://函数1Student(int height,int weight)//用户显式定义构造函数{_height=height;_weight=weight;}//函数2,无参Student()//构成重载{;}
private:int _height=170;int _weight=60;
};int main()
{Student s1(64,177);//创建时自动调用构造函数1Student s2;//创建时自动调用构造函数2//s2后面不需要跟括号,即不能写成 Student s2(); 否则就成了函数声明
}
当用户没有显式定义构造函数时,编译器会自动生成一个无参的默认构造函数,该默认构造函数对成员变量的处理方式为:对内置类型成员不做处理,对自定义成员调用其默认构造函数 (默认构造函数只有无参的构造函数、全缺省的构造函数、编译器自动生成的构造函数3种)。
class Date
{
public:Date(int year)//用户显式定义了一个参数不是缺省值的构造函数{_year=year;}
private:_year;
};class Student
{
pubilc://用户没有显示定义构造函数,由编译器生成默认构造函数
private:int* p;//不处理int _height=170;//不处理int _weight=60;//不处理Date d1;//调用Date类的默认构造函数//由于Date类没有默认构造函数,故出错
};
默认构造函数只能有一个
class Date
{
public:Date(int year=1){cout << "有参数" << endl;}Date (){cout << "无参数" << endl;}
private:int _year = 60;int _month = 0;int _day = 0;
};class Student
{
public:private:int height;int weigth;Date d;
};int main()
{Date d1(1);//调用有参的构造函数Date d2;//对重载的构造函数调用不明确,出错Student s;//自定义类型调用其默认构造时不明确,出错
}
由上我们可以得知当成员变量都是内置类型时构造函数可以不写,但大多数情况下都要写构造函数。
析构函数
析构函数的功能与构造函数的功能相反,用于对成员变量的资源的清理,在对象销毁时会自动调用析构函数,析构函数不能重载,参数列表为空。
class Date
{
public:Date(){_s=new int;}~Date()//析构函数{delete s;//进行资源清理}
private:int*_s;
}
当用户没有显式定义析构函数时,同构造函数一样,编译器会自动生成一个默认析构函数,该默认析构函数对成员变量的处理方式为:对内置类型成员不做处理,对自定义成员调用其析构函数。
当类里面没有资源申请时,析构函数可以不写,使用编译器生成的默认析构函数就可以了。
拷贝构造函数
在用已经存在的类对象创建新对象时会调用拷贝构造函数,如当参数传值为一个类对象、返回一个类对象时等。拷贝构造函数是构造函数的一个重载形式,参数只有一个且必须是类类型对象的引用。
class Date
{
public://参数一定要是引用,如果不是引用,使用拷贝构造函数要进行值拷贝,//就会又去调用拷贝构造函数,从而引发无穷递归Date(const Date& d)//拷贝构造函数,使用const使代码更健壮{_year=d._year;_month=d._month;_day=d._day;}private:int _year = 60;int _month = 0;int _day = 0;
};int main()
{Date d1;Date d2=d1;//调用拷贝构造函数
}
如果用户没有显式定义拷贝构造函数,默认的拷贝构造函数进行的是浅拷贝(值拷贝),对成员变量的处理方式为:对内置类型成员进行浅拷贝,对自定义成员调用其拷贝构造函数。
如果类里面没有涉及到资源的申请时,拷贝构造函数可以不写,但当涉及到资源申请时,拷贝构造函数一定要写,否则是浅拷贝,容易出错。
class Date
{
public:Date()//构造函数{_s=new int;}~Date()//析构函数{delete s;//进行资源清理}
private:int*_s;
}int main()
{Date d1;Date d2=d1;//不会再调用构造函数//以上代码运行时出错//Date类里面进行的是浅拷贝,d1和d2里面的_s指向同一块空间//对象d1、d2销毁时都要调用其析构函数,对同一块空间释放了2次,出错return 0;
}
赋值运算符重载
赋值运算符重载
当要对一个已经创建好的对象进行赋值操作时,需要调用赋值运算符重载函数
class Date
{
public:Date& operator=(const Date& d)//赋值运算符重载{if(this!=&d){_year=d._year;_month=d._month;_day=d._day;}return *this;}
private:_year;_month;_day;
}int main()
{Date d1;Date d2;d2=d1;//调用赋值运算符重载
}
该函数要注意以下4点:
1.为了符合连续赋值,函数需要返回*this,同时为了提高返回的效率,需要用到引用返回。
2.为了提升传参效率和增强代码健壮性,参数应为引用且使用const修饰。
3.要检测是否是自己给自己赋值。
原因可以参考这里l1dian11的博客
4.赋值运算符只能重载成类的成员函数,不能重载成全局函数。
当用户没有显式定义时,编译器会自动生成一个默认的运算符重载,以值的方式逐字节拷贝,对成员变量的处理方式为:对内置类型成员直接赋值,对自定义成员调用其对应的赋值运算符重载。
运算符重载
c++除了支持赋值运算符重载外,还支持其他的运算符重载,只不过编译器不会自动生成这些运算符重载,需要用户显式定义。
其有以下几点需要注意:
1.只能重载已有的运算符,不能通过其他符号重载新的运算符,如不能重载@
2.重载类型必须有一个类类型参数(防止用户改变该操作符原来的对内置类型的运算)
3.以下5个运算符不能重载:
.* :: sizeof ?: .
这里说一下比较特殊的运算符重载:
1.前置++和后置++重载
由于这两个运算符的重载无法直接区分,c++进行了特殊处理:在参数列表增加一个int型参数表示后置++
class Date
{
public:Date operator++()//表示前置++运算符重载{//...}Date operator++(int)//表示后置++运算符重载{//...}
private:_year;_month;_day;
};
2.流插入<<和流提取>>的运算符重载

cout是ostream类的对象,cin是istream类的对象
class Date
{
public:ostream& operator<<(ostream& out){//...}private:_year;_month;_day;
};
//用法如下
Date d;
d<<cout;
虽然重载成功了,因为this指针默认占了第一个参数,所以其使用方式很奇怪,不符合我们使用的习惯,因此我们只能将其重载成全局函数。
class Date
{
public://使用友元使类外的函数可以访问类里面的私有成员friend ostream& operator<<(ostream& out,const Date& d);
private:_year;_month;_day;
};friend ostream& operator<<(ostream& out,const Date& d)
{//...
}
const成员
大多数情况下我们并不希望成员函数拥有对类里面的成员进行修改的权限,因此我们希望对this指针使用const进行修饰。
class Date
{
public:void fun() const{//...}以上函数相当于void fun(const Date* const this)//第2个const是this指针自带的
private:_year;_month;_day;
};
我们建议只要成员函数不涉及到对成员变量的修改,后面都要加上const进行修饰。
取地址操作符重载
用于对一个普通对象取地址
class Date
{
public:Date* operator&(){return this;//一般是直接返回this即可//如果写成return 0x11223344;//那么用取地址符获取对象地址时将全都是0x11223344这个地址}
private:_year;_month;_day;
};
这个一般不需要重载,使用编译器默认生成的即可。
const取地址操作符重载
用于对const修饰的对象取地址
class Date
{
public:const Date* operator&() const{return this;//一般是直接返回this即可//如果写成return 0x11223344;//那么用取地址符获取对象地址时将全都是0x11223344这个地址}
private:_year;_month;_day;
};
这个一般也不需要重载,使用编译器默认生成的即可。
初始化列表
类在实例化成对象时,所有的成员变量都会在初始化列表中进行定义并给予变量相对应的值,内置类型如果没有显式地写在初始化列表,则会在初始化列表中给予其一个随机值,对自定义类型,会去调用其默认构造函数。初始化列表和构造函数可以混合使用。
c++11打了补丁,允许其在声明时赋值,这些值其实都是缺省值,用于给初始化列表。
class Date
{
public:Date(int year,int month,int day):_year(2),_month(2){_day=2;}
private:_year=1;_month=1;_day=1;
};int main()
{Date d(3,3,3);//d._year=2,d._month=2,d._day=2;
}
类里面成员变量在类中的声明次序就是初始化列表的初始化顺序,与其在初始化列表中的顺序无关。
explicit关键字
如果类的构造函数只有一个参数或者除第一个参数无默认值其余均有默认值,则该类可以支持隐式转换。
class Date
{
public:Date(int year,int month=1,int day=1){_year=year;_month=month;_day=day;}
private:_year;_month;_day;
};int main()
{Date d=2019;//_year=2019,_month=1,_day=1//将2019转换成Date(2019,1,1),再赋给d
}
c++11还支持多参数的隐式类型转换
class Date
{
public:Date(int year,int month,int day=1){_year=year;_month=month;_day=day;}
private:_year;_month;_day;
};int main()
{Date d={2019,10,11};//_year=2019,_month=10,_day=11
}
有时候我们并不希望这种隐式类型转换的发生,只需在构造函数前面加上explicit关键字即可,但这个关键字不能阻止强制类型转换的发生。
static成员
在类里面以static关键字修饰的成员称为类的静态成员,对于静态成员变量,其只能在类里面进行声明,不能给缺省值。
静态成员有以下特性:
1.静态成员为所有类对象所共享,不属于某个对象,存放在静态区。
2.静态成员变量在类内只是声明,必须要在类外定义,定义时不需加static关键字。
3.静态成员可以直接通过类名::静态成员或者对象.静态成员来访问
4.静态成员没有this指针,不能访问任何非静态成员。
6.静态成员也是类的成员,受public、private、protect访问限定符的限制。
class Date
{
public:explicit Date(int year=1,int month=1,int day=1){++i;_year = year;_month = month;_day = day;}static int i;
private:int _year;int _month;int _day;};int Date::i = 0;int main()
{Date d1;Date d2;Date d3;Date d4;Date d5;cout << Date::i << endl;//i=5;return 0;
}
匿名对象
c++允许匿名对象,可以拥有充当临时变量的作用,其生命周期只在这一行,该行执行完就销毁。
class Date
{
public:Date(int year,int month,int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;};int main()
{Date d1 = Date(1, 2, 3);//匿名对象Date d2 = {1,2,3};//隐式转换return 0;
}
友元
友元分为友元函数和友元类
友元函数是定义在类外部的普通函数,不属于任何类,在类里面声明,可以直接访问类的私有成员,其有以下特性:
1.友元函数不能用const修饰
2.友元函数可以定义在类定义的任何地方声明,不受访问限定符的限制
3.一个函数可以是多个类的友元函数
4.友元函数与普通函数的调用原理相同
友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类里面的私有成员。
友元关系是单向的,没有传递性,不能继承。
虽然友元为访问私有成员提供了便利,但破坏了封装,增加了耦合度,不建议过多使用。
内部类
内部类是指定义在另一个类内部的类,他是一个独立的类,不属于外部类,也不能通过外部类的对象访问内部类的成员,可以认为外部类对内部类没有任何优越的访问权限。但内部类却是外部类的友元类,即内部类可以通过类外部的对象参数访问外部类的所有成员。
需要注意外部类的大小和内部类没有任何关系。
拷贝对象时编译器的优化
大部分编译器会对连续的构造或拷贝构造进行优化,如
连续的构造+构造优化为一个构造
连续的构造+拷贝构造优化为一个构造
连续的拷贝构造+拷贝构造优化为一个拷贝构造
不同的编译器的优化方式和程度不同。
相关文章:
类和对象学习笔记
类和对象 类的定义this指针类的6个默认成员函数构造函数析构函数拷贝构造函数赋值运算符重载赋值运算符重载运算符重载const成员 取地址操作符重载const取地址操作符重载 初始化列表explicit关键字static成员匿名对象友元内部类拷贝对象时编译器的优化 类的定义 c类的定义形式…...
Linux程序之可变参数选项那些事!
一、linux应用程序如何接收参数? 1. argc、argv Linux应用程序执行时,我们往往通过命令行带入参数给程序,比如 ls /dev/ -l 其中参数 /dev/ 、-l都是作为参数传递给命令 ls 应用程序又是如何接收这些参数的? 通常应用程序都…...
【1day】泛微e-office OA系统user_page接口未授权访问漏洞学习
注:该文章来自作者日常学习笔记,请勿利用文章内的相关技术从事非法测试,如因此产生的一切不良后果与作者无关。 目录 一、漏洞描述 二、影响版本 三、资产测绘 四、漏洞复现...
Midjourney绘画提示词Prompt参考教程
Midjourney绘画提示词Prompt参考教程:无需魔法使用。 一、AI工具 SparkAi: SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统,支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常…...
Altium Designer学习笔记6
原理图库的制作,SMA元件的制作: 图形不是很重要,重要的是管脚的功能。 Design Item ID和Designator两个值是要注意的。 进行Place放置,切换到原理图工作区,测试下功能。 AD9851元件库制作: 不需要再新建原…...
【问题解决】Maven密码加密
普通的maven部署方式是把maven私服的账号密码以明文的方式配置在settings.xml文件中 <server><id>deploymentRepo</id><username>xxx</username><password>123</password></server> 这种方式的配置很容易被别人看到从而泄漏…...
vue3基于vite打包
大的js和css打包成:gzip npm install vite-plugin-compression 在vite.config.ts中配置: import viteCompression from vite-plugin-compressionplugins:[viteCompression({verbose:true,//控制台输出压缩结果disable:false,//开启还是禁用threshold:…...
前端使用JWT时无法获取Authorization请求头
是因为后端没有解决跨域问题: 补充下面的代码即可 response.setHeader("Access-Control-Expose-Headers", "Authorization");...
postman定义公共函数这样写,测试组长直呼牛逼!!!
postman定义公共函数 在postman中,如下面的代码: 1、返回元素是否与预期值一致 var assertEqual(name,actual,expected)>{tests[${name}:实际结果: ${actual} , 期望结果:${expected}]actualexpected…...
单元测试实战(六)其它
为鼓励单元测试,特分门别类示例各种组件的测试代码并进行解说,供开发人员参考。 本文中的测试均基于JUnit5。 单元测试实战(一)Controller 的测试 单元测试实战(二)Service 的测试 单元测试实战&am…...
jmeter 主从配置
配置 主从需要在同一子网。 可以修改从机的端口,主要是防火墙的原因,这样就可以在防火墙只配置一个端口,如果没有防火墙,可以不用配置 server_port1099 server.rmi.localport1099 centos防火墙开端口 sudo firewall-cmd --zonep…...
4
【任务 1】容器云平台搭建[5 分] 【题目 1】平台部署–部署容器云平台[1.5 分] 【题目 2】平台部署–部署 Istio 服务网格[0.5 分] 【题目 3】平台部署–部署 KubeVirt 虚拟化[1 分] 【题目 4】平台部署–部署 Harbor 仓库及Helm 包管理工具[1 分] 【题目 5】集群管理–备份 ET…...
C++ Day04 this指针,友元函数,重载
this指针 概念 谁调用 this 所在的函数 ,this 就存储谁的地址 特点 1, 在当前类的非静态成员函数中调用本类非静态成员时 , 默认有 this 关键字 2, 静态成员函数 , 没有 this 指针。 示例 #include <iostream> #include <cstring> using namespace std; class S…...
2023做车载测试真的可以远离内耗!转行车载月入20K!
2023年,车载测试正处于一个发展阶段,随着新能源汽车的蓬勃发展,电气化、智能化逐渐成为发展趋势。在汽车开发过程中,测试是非常重要的一个环节。现在软件越来越多地被应用到汽车上,对软件测试的需求也越来越多、越来越…...
LINUXZ
10.6.2 AT24C02 访问方法 设备地址 从芯片手册上可以知道,AT24C02 的设备地址跟它的 A2、A1、A0 引脚有关: 图 10.36 AT24C02 设备地址引脚配置 294 / 577 打开 I2C 模块的原理图: 开发板配套网盘资料\04_开发板原理图\ 04_Extend_modules\通…...
C#单例模式懒汉式与饿汉式
单例模式一般分为懒汉模式和饿汉模式,懒汉式单例在第一次引用时创建实例,不是在类加载时;饿汉式单例模式是一种在类加载时就创建实例的方式,因此也称为静态初始化。 单例模式实现的技巧时构造私有,向外提供静态实例。…...
小微初创企业,如何利用媒体宣传快速成长
传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 对于小微初创企业来说,利用媒体宣传可以快速提升品牌知名度、扩大影响力,进而促进企业的成长。 1.确定宣传目标:是增加销售、提升品牌知名度、还是推…...
【封装UI组件库系列】全局样式的定义与重置
封装UI组件库系列第二篇样式 🌟前言 🌟定义全局样式 生成主题色和不同亮度的颜色 编辑 中性色及其他变量 🌟样式重置 🌟总结 🌟前言 在前端开发中,大家…...
opencv将32位深图片合成视频跳帧解决办法
在合成视频时候,大多数的图片都是24位深度的(即RGB三通道,一个通道8位),但是也存在少量的32位深的图片(RGBA,三个颜色通道加上A这个透明度通道),32位和24位的格式是不一样…...
华为obs上传下载-Java版 2023-11-23
弄了半天,老师帮弄成功了,经过同意,分享到网上,希望能帮助更多人,至于怎么弄的,我也不知道。 创建idea项目后,项目结构,对应文件没有的创一个 pom.xm 注意改Java版本,我…...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
vue3 daterange正则踩坑
<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...
react更新页面数据,操作页面,双向数据绑定
// 路由不是组件的直接跳转use client,useEffect,useRouter,需3个结合, use client表示客户端 use client; import { Button,Card, Space,Tag,Table,message,Input } from antd; import { useEffect,useState } from react; impor…...
