C++:类和对象

发布时间:2026/10/24 9:33:55

C++:类和对象 目录1.类的定义一、类定义格式二、访问限定符三、类域2.实例化一、实例化概念二、对象大小内存对齐规则:3.this指针4.类的默认成员函数一、构造函数构造函数的特点:注意:二、析构函数析构函数特点三、拷贝构造函数拷贝构造函数特点四、赋值运算符重载五、取地址运算符重载operator 六、const取地址运算符重载operator () const5.再探构造函数6.static成员7.友元8.内部类9.匿名对象1.类的定义一、类定义格式class为定义类的关键字Stack为类的名字{}中为类的主体。类体中内容称为类的成员类中的变量称为类的属性或成员变量; 类中的函数称为类的⽅法或者成员函数。C中struct也可以定义类C兼容C中struct的⽤法同时struct升级成了类明显的变化struct中可以定义函数⼀般情况下我们还是推荐⽤class定义类。定义在类⾯的成员函数默认为inline。class A { public: void Init() { _a 10; } void Print() { cout _a endl; } private: int _a; };二、访问限定符public修饰的成员在类外可以直接被访问protected和private修饰的成员在类外不能直接被访问protected和private是⼀样的。访问权限作⽤域从该访问限定符出现的位置开始直到下⼀个访问限定符出现时为⽌如果后⾯没有 访问限定符作⽤域就到 }即类结束。class定义成员没有被访问限定符修饰时默认为privatestruct默认为public。⼀般成员变量都会被限制为private/protected需要给别⼈使⽤的成员函数会放为public。class A { public: void Init() { _a 10; } void Print() { cout _a endl; } private: int _a; }; int main() { A a; a.Init(); a.Print(); //a._a;//运行时会报错因为在类外面无法访问private中的成员 return 0; }运行结果10三、类域用class/struct大括号{}包裹起来的区域就是类域。类定义了⼀个新的作⽤域类的所有成员都在类的作⽤域中在类体外定义成员时需要使⽤ :: 作用域操作符指明成员属于哪个类域。class A { public: void Init(); void Print();//声明 private: int _a; }; void A::Init()//A:: 表示Init属于A这个类域 { _a 10; } void A::Print()//定义 { cout _a endl;2.实例化一、实例化概念⽤类类型在物理内存中创建对象的过程称为类实例化出对象。class Date 只是一个类型定义编译时只记录类域成员不分配对象内存class Date { public: void Init(int year, int month, int day) { _year year; _month month; _day day; } void Print() { cout _year / _month / _day endl; } private: //这里只是声明没有开空间 int _year; int _month; int _day; }; int main() { Date d1;//Date类实例化出对象d1 d1.Init(2026, 10, 20); d1.Print(); return 0; }二、对象大小类实例化之后对象占用内存字节数只算非静态成员变量成员函数、静态成员变量不算在对象大小里面放在静态区里面不属于对象内存统计时只统计非static成员变量并遵循内存对齐规则。内存对齐规则:第⼀个成员在与结构体偏移量为0的地址处。其他成员变量要对⻬到某个数字对⻬数的整数倍的地址处。注意对⻬数 编译器默认的⼀个对⻬数 与 该成员⼤⼩的较⼩值。VS中默认的对⻬数为8结构体总⼤⼩为最⼤对⻬数所有变量类型最⼤者与默认对⻬参数取最⼩的整数倍。如果嵌套了结构体的情况嵌套的结构体对⻬到⾃⼰的最⼤对⻬数的整数倍处结构体的整体⼤⼩就是所有最⼤对⻬数含嵌套结构体的对⻬数的整数倍。class Date { public: void Init(int year, int month, int day) { _year year; _month month; _day day; } void Print() { cout _year / _month / _day endl; } private: int _year; int _month; int _day; }; int main() { Date d1; cout sizeof(d1) endl;//计算d1的大小 return 0; }运行结果123.this指针编译器编译后类的成员函数默认都会在形参第⼀个位置增加⼀个当前类类型的指针叫做this 指针。⽐如Date类的Init的真实原型为 void Init(Date* const this, int year, int month, int day);类的成员函数中访问成员变量本质都是通过this指针访问的如Init函数中给_year赋值,this- _year year;C规定不能在实参和形参的位置显⽰的写this指针(编译时编译器会处理)但是可以在函数体内显 ⽰使⽤this指针。class Date { public: void Init(int year, int month, int day) { //this nullptr;//编译时会报错因为这里this的类型是Date cont * this this-_year year; this-_month month; this-_day day; } void Print() { cout _year / _month / _day endl; } private: int _year; int _month; int _day; }; int main() { Date d1; d1.Init(2026, 1, 1); d1.Print(); return 0; }4.类的默认成员函数默认成员函数就是⽤⼾没有显式实现编译器会⾃动⽣成的成员函数称为默认成员函数。⼀个类我们不写的情况下编译器会默认⽣成以下6个默认成员函数一、构造函数构造函数是特殊的成员函数需要注意的是构造函数虽然名称叫构造但是构造函数的主要任务并 不是开空间创建对象(我们常使⽤的局部对象是栈帧创建时空间就开好了)而是对象实例化时初始化对象。构造函数的特点:函数名与类名相同。无返回值。对象实例化时系统会自动调用对应的构造函数。构造函数可以重载。如果类中没有显式定义构造函数则C编译器会自动⽣成⼀个无参的默认构造函数⼀旦用户显式定义编译器将不再⽣成。无参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数都叫做默认构造函 数。但是这三个函数有且只有⼀个存在不能同时存在。⽆参构造函数和全缺省构造函数虽然构成 函数重载但是调⽤时会存在歧义。class Date { public: Date()//构造函数 { _year 1; _month 1; _day 1; } Date(int year, int month, int day)//函数重载 { _year year; _month month; _day day; } void Print() { cout _year / _month / _day endl; } private: int _year; int _month; int _day; }; int main() { Date d1; Date d2(2026,1,1); d1.Print(); d2.Print(); return 0; }运行结果1/1/1 2026/1/1注意:调用无参构造时“不要写括号”,写了括号时就相当于时一个函数声明编译时会报错。//Date d3();//函数声明Date d3(void);我们不写编译器默认⽣成的构造对内置类型成员变量的初始化没有要求也就是说是是否初始化是不确定的看编译器。class Date { public: void Print() { cout _year / _month / _day endl; } private: int _year; int _month; int _day; }; int main() { Date d1; d1.Print(); return 0; }运行结果-858993460/-858993460/-858993460对于自定义类型成员变量要求调⽤这个成员变量的默认构造函数初始化。如果这个成员变量没有默认构造函数那么就会报错。二、析构函数析构函数与构造函数功能相反析构函数不是完成对对象本身的销毁比如局部对象是存在栈帧的函数结束栈帧销毁他就释放了不需要我们管C规定对象在销毁时会自动调用析构函数完成对象中资源的清理释放工作。析构函数特点析构函数名是在类名前加上字符 ~。无参数无返回值。一个类只能有一个析构函数。若未显示定义系统会自动生成默认的析构函数。对象生命周期结束时系统会自动调用析构函数。跟构造函数类似我们不写编译器自动生成的析构函数对内置类型成员不做处理自定类型成员会调用他的析构函数。还需要注意的是我们显⽰写析构函数对于自定义类型成员也会调用他的析构也就是说⾃定义类型成员无论什么情况都会⾃动调用析构函数。如果类中没有申请资源时析构函数可以不写直接使⽤编译器⽣成的默认析构函数如Date。class stack { public: stack(int n 4)//构造函数 { int* tmp (int*)malloc(sizeof(int) * n); if (tmp nullptr) { perror(malloc); exit(1); } arr tmp; top 0; capacity n; } ~stack()//析构函数 { cout~stackendl; free(arr); arr nullptr; top capacity 0; } private : int* arr; int top; int capacity; }; int main() { stack st; return 0; }运行结果~stack三、拷贝构造函数拷贝构造函数是一种特殊的构造函数用一个已经存在的同类对象去初始化新建对象拷贝构造函数特点拷贝构造函数的第⼀个参数必须是类类型对象的引用使用传值方式编译器直接报错因为语法逻辑上会引发无穷递归调用。 拷贝构造函数也可以多个参数但是第⼀个参数必须是类类型对象的引 用后面的参数必须有缺省值。拷贝构造函数是构造函数的⼀个重载。C规定自定义类型对象进⾏拷贝行为必须调用拷贝构造所以这里自定义类型传值传参和传值返回都会调⽤拷贝构造完成。若未显式定义拷贝构造编译器会⽣成自动生成拷贝构造函数。自动生成的拷贝构造对内置类型成员变量会完成值拷贝/浅拷贝(⼀个字节⼀个字节的拷贝)对⾃定义类型成员变量会调用他的拷贝构造。class Date { public: Date()//构造函数 { _year 1; _month 1; _day 1; } Date(int year, int month, int day)//函数重载 { _year year; _month month; _day day; } Date(Date d)//拷贝构造 { _year d._year; _month d._month; _day d._day; } void Print() { cout _year / _month / _day endl; } private: int _year; int _month; int _day; }; int main() { Date d1(2026, 1, 1); Date d2 d1; Date d3(d1); d1.Print(); d2.Print(); d3.Print(); return 0; }运行结果2026/1/1 2026/1/1 2026/1/1注意因为Date类成员变量是内置类型当我们不写拷贝构造的时候会默认使用系统的拷贝构造也就是完成浅拷贝一个字节一个字节的拷贝运行出的结果和写了拷贝构造的效果是一样的。自定义类型的拷贝构造class stack { public: stack(int n 4) { int* tmp (int*)malloc(sizeof(int) * n); if (tmp nullptr) { perror(malloc); exit(1); } arr tmp; top 0; capacity n; } ~stack() { free(arr); arr nullptr; top capacity 0; } stack(stack st) { arr (int*)malloc(sizeof(int) * st.capacity); if (arr nullptr) { perror(malloc); exit(1); } memcpy(arr, st.arr, sizeof(int) * st.top); top st.top; capacity st.capacity; } void Push(int n) { if (top capacity) { int newcapacity 2 * capacity; int* tmp (int*)realloc(arr, sizeof(int) * newcapacity); if (tmp nullptr) { perror(realloc); exit(1); } capacity newcapacity; arr tmp; } arr[top] n; } void Print() { for (int i 0; i top; i) { cout arr[i] ; } cout endl; } private : int* arr; int top; int capacity; }; int main() { stack st; st.Push(1); st.Push(2); st.Push(3); st.Push(4); st.Push(5); stack st1 st; st.Print(); st1.Print(); return 0; }运行结果1 2 3 4 5 1 2 3 4 5四、赋值运算符重载只能作为类的成员函数重载不能是全局函数。 运算符重载强制要求成员函数因为它需要隐式this指针左操作数必须是类对象。触发条件是两个对象已经存在进行对象赋值。class Date { public: Date(int year, int month, int day) { _year year; _month month; _day day; } Date operator (const Date d1) { if (this ! d1) { _year d1._year; _month d1._month; _day d1._day; } return *this; } void Print() { cout _year / _month / _day endl; } private: int _year; int _month; int _day; }; int main() { Date d1(2026, 1, 1); Date d2(2026, 2, 2); d1.Print(); d2.Print(); d2 d1;//赋值重载 //Date d3 d1;//这是拷贝构造不是赋值重载 d2.Print(); return 0; }五、取地址运算符重载operator 对非const对象取地址返回当前对象的this指针Date* operator() { return this; //return nullptr//返回空值 }六、const取地址运算符重载operator () const对const对象取地址。成员函数后面的const表示这个成员函数不会修改对象成员才能被 const 对象调用。Date* operator() const { return this; //return nullptr//返回空值 }5.再探构造函数之前我们实现构造函数时初始化成员变量主要使⽤函数体内赋值构造函数初始化还有⼀种方式就是初始化列表初始化列表的使⽤⽅式是以⼀个冒号开始接着是⼀个以逗号分隔的数据成员列表每个成员变量后⾯跟⼀个放在括号中的初始值或表达式。每个成员变量在初始化列表中只能出现⼀次语法理解上初始化列表可以认为是每个成员变量定义初始化的地方。引用成员变量const成员变量没有默认构造的类类型变量必须放在初始化列表位置进⾏初始化否则会编译报错。初始化列表中按照成员变量在类中声明顺序进行初始化跟成员在初始化列表出现的的先后顺序无关。建议声明顺序和初始化列表顺序保持⼀致。C11⽀持在成员变量声明的位置给缺省值这个缺省值主要是给没有显⽰在初始化列表初始化的成员使用的。class Date { public: Date() :_year(2026) { } void Print() { cout _year / _month / _day endl; } private: int _year ; int _month 1; int _day 1; }; int main() { Date d1; d1.Print(); return 0; }运行结果2026/1/16.static成员⽤static修饰的成员变量称之为静态成员变量静态成员变量⼀定要在类外进行初始化。静态成员变量为所有类对象所共享不属于某个具体的对象不存在对象中存放在静态区。用static修饰的成员函数称之为静态成员函数静态成员函数没有this指针。非静态的成员函数可以访问任意的静态成员变量和静态成员函数。突破类域就可以访问静态成员可以通过类名::静态成员 或者 对象.静态成员 来访问静态成员变量和静态成员函数。静态成员也是类的成员受public、protected、private 访问限定符的限制。静态成员变量不能在声明位置给缺省值初始化因为缺省值是个构造函数初始化列表的静态成员变量不属于某个对象不走构造函数初始化列表。class A { public: A() { _count; } ~A() { _count; } A(A a) { _count; } static int Getcount() { return _count; } private: static int _count;//类里面声明 }; int A::_count 0;//类外面初始化 int main() { A a; A b; A c b; cout A::Getcount() endl; return 0; }运行结果37.友元友元提供了⼀种突破类访问限定符封装的⽅式友元分为友元函数和友元类在函数声明或者类声明的前⾯加friend并且把友元声明放到⼀个类的里面。外部友元函数可访问类的私有和保护成员友元函数仅仅是⼀种声明他不是类的成员函数。友元函数可以在类定义的任何地⽅声明不受类访问限定符限制。⼀个函数可以是多个类的友元函数。友元类中的成员函数都可以是另⼀个类的友元函数都可以访问另⼀个类中的私有和保护成员。友元类的关系是单向的不具有交换性比如A类是B类的友元但是B类不是A类的友元。class B;//// 前置声明否则A的友元函数声明编译器不认识B class A { public: friend void func(const A a, const B b);//友元声明 private: int _a 1; int _b 2; }; class B { public: friend void func(const A a, const B b);//友元声明 private: int _c 3; int _d 4; }; void func(const A a, const B b) { cout a._a endl; cout b._c endl; } int main() { A a; B b; func(a, b); return 0; }运行结果1 3class A { public: friend class B;//友元声明 private: int _a 1; int _b 2; }; class B { public: void func(const A a) { cout a._a endl; cout _d endl; } private: int _c 3; int _d 4; }; int main() { A a; B b; b.func(a); return 0; }运行结果1 48.内部类如果⼀个类定义在另⼀个类的内部这个内部类就叫做内部类。内部类是⼀个独⽴的类跟定义在全局相比他只是受外部类类域限制和访问限定符限制所以外部类定义的对象中不包含内部类。内部类默认是外部类的友元类。内部类是独立的类只是作用域被限制在外层类里面内部类不默认拥有外部类的this指针。class A { public: class B//B默认就是A的友元 { public: void func(Aa) { cout a._a endl; cout _c endl; } private: int _c 3; int _d 4; }; private: int _a 1; int _b 2; }; int main() { A::B b; A a; b.func(a); return 0; }运行结果1 39.匿名对象用类型(实参) 定义出来的对象叫做匿名对象相比之前我们定义的类型对象名(实参) 定义出来的叫有名对象。匿名对象生命周期只在当前⼀行⼀般临时定义⼀个对象当前用一下即可就可以定义匿名对象。class A { public: A(int a 0) :_a(a) { cout A(int a) endl; } ~A() { cout ~A()endl; } A(const A a) { _a a._a; } private: int _a; }; int main() { //有名对象 A aa1; A aa2(10); //匿名对象--声明周期只在当前一行 A(); A(1); return 0; }运行结果A(int a)//aa1的构造 A(int a)//aa2的构造 A(int a)//A()的构造 ~A() //A()的析构 A(int a)//A(1)的构造 ~A() //A(1)的析构 ~A() //aa2的析构 ~A() //aa1的析构
延伸阅读

更多相关文章

2026/10/24 9:33:55

TTA详细学习

TENTTTA希望利用测试阶段获得的目标域数据让模型对其进行适应,用适应后的模型预测。但测试阶段没有真实标签,那应该用什么损失函数更新模型?1.Tent优化目标是最小化预测熵当预测集中在某个类别时,熵较低。Tent优化目标是让模型对目…

2026/10/24 9:28:54

Loop Engineering实战:构建可观测的自动化循环工程

1. 从零理解 Loop Engineering:它到底是什么,能解决什么问题第一次听到“Loop Engineering”这个词,很多人会以为是某种新的编程语言或者框架。其实不是。它更像是一种工程方法论,核心思想是把系统中那些需要反复执行、反复验证、…

2026/10/24 9:28:54

Docker容器启动命令覆盖原理与实战技巧

1. Docker容器启动时覆盖镜像命令的原理与实战当我们需要临时调整容器行为时,覆盖镜像默认命令是个高频操作。上周排查线上问题时,我就遇到个典型场景:某个基于Alpine的日志收集容器,其默认启动命令会直接运行采集程序&#xff0c…

2026/10/24 11:39:04

Wi-Fi认证测试套件命令体系全解析:从控制面地图到实战避坑

简介:Wi-Fi Test Suite Control API Specification v10.12.0 是 Wi-Fi 联盟发布的认证测试套件控制接口规范文档,面向从事 Wi-Fi 设备研发、认证测试与互操作性验证的工程师及测试人员,用于解决认证测试中控制端与代理端接口不统一、测试流程…

2026/10/24 11:39:04

CMOS设置入门:3分钟识别硬盘与设置启动顺序

简介:本资源是一份面向计算机初学者与硬件维护人员的CMOS/BIOS设置入门指南,系统讲解AWARD BIOS(当前主流台式机主板采用)各功能模块的操作逻辑与实用配置要点,帮助用户掌握开机参数调整、硬盘自动识别、电源管理、安全…

2026/10/24 11:39:04

GAF-PCNN-MATT:时序信号转图像分类的MATLAB实现与调优

简介:本资源面向具备一定深度学习与图像处理基础的研究人员和技术开发者,提供一套基于Matlab实现的GAF-PCNN-MATT图像分类预测方案,用于应对高维复杂特征下传统方法分类精度不足的问题。模型融合格拉姆角场、双通道脉冲耦合神经网络与多头注意…

2026/10/24 11:39:04

MATLAB/YALMIP建模:阶梯碳价与电制氢耦合调度实战

简介:本资源是一份面向能源系统优化研究者与工程师的MATLAB实战教程,聚焦阶梯型碳交易机制与电制氢技术融合下的综合能源系统24小时优化调度问题。内容涵盖系统建模(传统机组、风电、电解槽、储氢罐、燃料电池五单元耦合)、分段线…

2026/10/24 11:39:04

MetaShape Python API 深度解析:对象图镜像与摄影测量状态调试

简介:本资源是Agisoft官方发布的《MetaShape Python API 1.82 深入解析》PDF参考手册,面向使用Metashape Professional版进行摄影测量自动化处理的Python开发者、三维重建工程师及地理信息科研人员。手册全面覆盖API核心模块(如Document、Chu…

2026/10/24 11:29:04

Mininet三种创建方式:可视化拖拽、命令行速建与交互式调试

简介:本资源为高校计算机网络课程配套的Mininet基础实验报告,面向网络工程、软件定义网络(SDN)初学者及实践教学场景,聚焦轻量级网络仿真平台的核心操作能力培养。报告系统覆盖Miniedit可视化建模、命令行拓扑构建、交…

2026/10/23 22:56:35

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/24 0:02:40

MATLAB缩进快捷键与批量格式化实战指南

1. 被自动缩进折磨的那些年如果你写过MATLAB代码,大概率经历过这种场景:从某个文档、网页或者聊天记录里复制了一段代码,粘贴进编辑器之后,每一行前面都带着莫名其妙的空格,有的行首是四个空格,有的是一个T…

2026/10/24 0:02:40

用Poco实现Qt风格信号与槽的跨平台C++方案

简介:本资源是一份面向C中级开发者的技术实践工程,聚焦于在非Qt框架环境下模拟实现Qt核心通信机制——信号与槽,借助Poco网络库的事件系统完成解耦式对象通信。适用于需在跨平台C项目中引入松耦合回调机制、但受限于无法集成Qt依赖的开发场景…

2026/10/24 0:02:40

ESXi裸机虚拟化安装指南:从硬件兼容到虚拟机部署

1. 为什么还要折腾ESXi:从一台闲置机器说起手里攒了几台淘汰下来的迷你主机,配置不算差,i5加16G内存,扔了可惜,装个桌面系统又觉得浪费。这种场景下,ESXi就成了一个绕不开的选择。它是一套裸机虚拟化平台&a…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑