C++学习笔记(十:类与对象基础)

往篇内容:

 C++学习笔记(一)
        一、C++编译阶段※

        二、入门案例解析 

        三、命名空间详解

        四、C++程序结构

C++学习笔记(二)

        五、函数基础

        六、标识符

        七、数据类型

        补充:二进制相关的概念

                  sizeof 运算符简介

        补充:原码、反码和补码

C++学习笔记(三)

        补充:ASCII码表

        八、内存构成

         补充:变量

        九、指针基础 

        十、const关键字

        十一、枚举类型 

C++学习笔记(四)

        十二、 类型转换

        十三、define指令 

        十四、typedef关键字

        十五、运算符

        十六、 流程控制

C++学习笔记(六)

        十七、数组

C++学习笔记(七)

        十八、指针

C++学习笔记(八)

        十九、函数进阶

        二十、变量进阶

C++学习笔记(九)

        二十一、结构体

        二十二、联合

目录

二十三、类与对象

1、面向过程

2、面向对象

 3、类

4、对象

5、成员引用

 6、成员函数

 7、inline函数

 8、对象内存

 9、this指针

10、类与结构体的区别 

11、封装特性 


 

二十三、类与对象

1、面向过程

        面向过程程序设计(Procedural-Oriented Programming, POP)是一种以过程为中心的编程思想。它强调的是解决问题的步骤和流程,通过函数(或称为过程)来组织代码。

        在 C++ 中,虽然支持 OOP,但仍然兼容传统的面向过程编程方式,例如使用全局变量、函数等。

思想:先做什么,再做什么,即按照顺序执行一系列操作来完成任务。

特点: 

特性描述
程序结构由主函数调用多个函数组成
数据与行为分离数据和操作数据的函数是分开的
顺序执行为主强调流程控制,按步骤执行
全局变量常用于共享数据
函数重用可以通过函数复用代码逻辑

示例:

#include <iostream>
using namespace std;void add(int a, int b) {cout << "Sum: " << a + b << endl;
}int main() {int x = 5, y = 10;add(x, y); // 调用函数return 0;
}

2、面向对象

        面向对象程序设计(Object-Oriented Programming, OOP)是一种以对象为核心的编程思想。它将现实世界中的事物抽象为对象,每个对象包含数据(属性)和对数据的操作(方法)。

        C++ 是一种多范式语言,其中最强大的特性就是对 OOP 的完整支持。

思想:“万物皆对象,程序由一组相互协作的对象构成,每个对象封装了自身的状态和行为

四大核心特性: 

概念描述
封装(Encapsulation)将数据和操作封装在一个类中,对外隐藏实现细节
继承(Inheritance)子类可以继承父类的属性和方法,实现代码复用
多态(Polymorphism)同一个接口可以有多种实现,运行时决定具体行为
抽象(Abstraction)提取共性特征,忽略复杂实现细节

示例:

#include <iostream>
using namespace std;class Rectangle {
private:int width, height;public:Rectangle(int w, int h) : width(w), height(h) {}int area() {return width * height;}
};int main() {Rectangle rect(5, 10);cout << "Area: " << rect.area() << endl;return 0;
}

 3、类

        类(class是用户自定义的数据类型,用于封装数据(属性)和操作这些数据的方法(行为)。它是面向对象编程(OOP)的核心概念之一。

        你可以把类看作是蓝图模板,而对象则是根据这个模板创建出来的具体实例。

定义格式: 

class 类名{

        成员访问限定符: //private,public,protected三种取值

                [数据成员s];

                [成员函数s];

};

 成员访问限定符:用来指定个数据成员的访问属性

  • public :类内类外都可以访问
  • private :只能被本类的成员函数引用,类外不能调用(特殊情况后面再讨)
  • protected :和private类似,派生时情况不同

注意事项:

  • 成员访问限定符可以出现多次
  • 作用范围:从当前限定符到下一个限定符或到类的 }

示例:

class Student {

private:

        string name;

        int age;

public:

        void setName(string n) { name = n; }

        void setAge(int a) { age = a; }

        void printInfo() {

                cout << "Name: " << name << ", Age: " << age << endl;

        }

};

注意事项: 

  • 声明或定义类类型时不分配内存空间,用类类型实例创建对象时,分配内存空间
  • 不指定成员限定符,默认为 private
  • 定义类对象的方式有3种,与结构体类似
  • 类的成员函数只能由类的对象或指针调用(特殊情况后续讨论)

4、对象

  • 客观世界中任何事物都可以看成对象,对象由属性和方法组成,每个对象都具有静态的属性和动态的功能。
  • 具有相同属性和功能的对象,我们可以把它们归为一类,故而我们可以得出结论:类是对象的抽象,对象是类的实例。

抽象概念理解:抽象的过程是将有关事物的共性归纳、集中的过程。

定义对象格式:


class 类名 对象名;

类名 对象名; //推荐

//定义类的同时实例化对象

class 类名{

        数据成员s;

        成员函数s;

}对象1,对象2...对象n;

//定义匿名类,同时实例化对象

class {

        数据成员s;

        成员函数s;

}对象1,对象2...对象n;

案例:

#include <iostream>
#include <cstring>
using namespace std;// 定义类类型
class Student {// 默认权限修饰符为 private// 成员函数,一般 public
public:void disp() {cout << "id: " << id << endl;cout << "name: " << name << endl;cout << "age: " << age << endl;}// 权限修饰符,一般数据成员 private// private:
public:int id;char name[20];int age;
} s3 = {1020, "lucy", 21}; // 列表初始化依旧有用int main() {// 由类类型实例化对象【定义变量】Student s1;s1.id = 1001;// s1.name = "tom"; error,详见字符串数组strcpy(s1.name, "tom");s1.age = 20;// 类对象调用成员函数Student s2 = s1; // 修正:删除了多余的class关键字s2.disp();cout << "--------------" << endl;s3.disp();return 0;
}

 注意:对象初始化可以采用列表的方式进行,数据成员不能为private。

5、成员引用

使用类对象调用数据成员或成员函数有2种方式:

  • 对象名.成员名;
  • 类指针变量->成员名;

案例:

#include <iostream>
using namespace std;// 自定义结构体类型
class Student {
public:int num;char name[20];char sex;int age;float score;char addr[30];public:// 成员函数必须通过类对象或指针来调用// 对象名.disp(); 指针变量->disp();void disp() {cout << "Student: [" << num << "," << name << "," << sex<< "," << age << "," << score << "," << addr << "]" << endl;}
};// 值传递
void printStudent(Student s) {cout << "Student: " << s.num << "," << s.name << "," << s.sex << "," << s.age << "," << s.score << "," << s.addr << endl;
}// 指针传递
void printStudent(Student *p) {cout << "Student: {" << p->num << "," << p->name << "," << p->sex << "," << p->age << "," << p->score << "," << p->addr << "}" << endl;
}// 引用传递(注意:不能与值传递同名,会产生二义性)
void printStudent2(Student &s) {cout << "Student: (" << s.num << "," << s.name << "," << s.sex << "," << s.age << "," << s.score << "," << s.addr << ")"<< endl;
}int main() {// 初始化Student s = {2001, "Tom", 'M', 19, 78.5, "平遥学院路56号"};printStudent(s);    // 值传递printStudent2(s);   // 引用传递Student *ps = &s;printStudent(ps);   // 指针传递cout << "---------------------" << endl;s.disp();           // 成员函数调用return 0;
}

 6、成员函数

        类的成员函数(member function是类的重要组成部分之一。它们定义了类的行为,即对象可以执行的操作。

        它的用法和作用和之前学习的函数基本上是一样的,也有返回值和函数类型,它与一般函数的区别只是: 它是属于一个类的成员,出现在类体中。

        在使用类函数时,要注意调用它的权限 (它能否被调用 )以及它的作用域 (函数能使用什么范围中的数据和函数 )

成员函数定义方式

① 在类内定义(隐式内联)

class Circle {

private:

        double radius;

public:

        void setRadius(double r) { radius = r; } // 类内定义

        double getArea() { return 3.14159 * radius * radius; }

};

⚠️ 这种方式适合简单函数,编译器会尝试将其优化为内联函数 (inline)。

 ② 在类外定义(推荐)

class Circle {

private:

        double radius;

public:

        void setRadius(double r); // 函数声明

        double getArea();

};

// 类外实现

// 返回值类型 类名::函数名(参数列表);

void Circle::setRadius(double r) {

        radius = r;

}

double Circle::getArea() {

        return 3.14159 * radius * radius;

}

推荐这种方式:代码更清晰、结构更合理,便于维护。 

注意:

如果函数名前面既无类名又无 ,如 display() display() 这表示 display 函数不属于任何类,这个函数不是成员函数,而是全局函数,即非成员函数的一般普通函数。

③ 成员函数的调用 

成员函数可以通过对象或指针来调用:

Circle c;

c.setRadius(5.0); // 通过对象调用

c.printInfo();

Circle* pc = &c;

pc->setRadius(10.0); // 通过指针调用

④ const成员函数

如果你希望某个成员函数不修改类成员变量的值,那可以将其声明为const

class Rectangle {

private:

        int width, height;

public:

        int area() const {

                return width * height; // 不允许修改成员变量

}

        //获取周长

        int perimeter() const;

};

//类外定义const函数,一定要跟上const修饰

int Rectangle::perimeter() const

{

        //width = 2; error

        return 2 * (width + height);

}

🔒 注意:const 成员函数只能访问其他 const 成员函数,不能修改任何数据成员。

 7、inline函数

        为了减少时间开销,如果在类体中定义的成员函数中不包括循环等控制结构,C++系统会自动将它们作为内联 inline 函数来处理。

class Circle {

private:

        double radius;

public:

        //类内定义,即使没有inline声明,默认也为inline函数

        void setRadius(double r) { radius = r; }

        double getArea() { return 3.14159 * radius * radius; }

};

在调用 setRadiusgetArea 时,并不真正执行函数调用过程 ,而是将函数代码嵌入程序的调用点,大大减少了调用成员函数的时间开销。

注意1:

如果成员函数在类体外定义**,系统并不把它默认为 inline 函数

注意2

如果要在类体外定义 inline 函数,需要在函数定义前加上 inline ,还应类定义和成员函数的定义都放在同一个头文件中 (或者写在同一个源文件中),否则编译时无法进行代码嵌入。例如:

 //函数定义时加inline,声明时则不需要

inline void CirclesetRadius(double r)

{

        radius = r;

}

弊端:上述做法,不利于类的接口与类的实现分离,不利于信息隐蔽。虽然程序的执行效率提高了,但从软件工程质量的角度来看,这样做并不是好的办法。

 8、对象内存

        用类去定义对象时,系统会为每一个对象分配存储空间。每个对象占用内存空间大小,只取决于数据成员和对齐方式,和成员函数没有任何关系。

案例: 

#include <iostream>
#include <cstddef> // for alignof
using namespace std;class MyClass {
public:int x;double y;char z;void disp() {cout << "x: " << x << ", y: " << y << ", z: " << z << endl;}
};int main() {cout << "Alignment of MyClass: " << alignof(MyClass) << " bytes" << endl;cout << "Size of MyClass: " << sizeof(MyClass) << " bytes" << endl;MyClass obj;obj.x = 10;obj.y = 3.14;obj.z = 'A';obj.disp();MyClass obj2;obj2.x = 3;obj2.y = 2.5;obj2.z = 'x';obj2.disp();return 0;
}
思考:为什么调用不同对象成员函数时,执行完全相同的一段函数代码,但是执行结果是不相同的?

答:因为成员函数可以访问和操作对象的实例数据(属性)。每个对象的属性值是独立存储的,因此即使函数代码相同,执行时基于不同的属性值会产生不同的结果。

 9、this指针

每个非静态成员函数都有一个隐藏参数: this ,它是一个指向当前对象的指针。

  • this 是指向调用该函数的对象的指针
  • 常用于解决形参与成员变量同名的问题
  • 可以用来返回当前对象(常用于链式调用)

案例:

#include <iostream>
#include <string>  // 补充缺失的头文件
using namespace std;class MyClass {
public:string name;int x;double y;char z;void disp() {// this指向成员函数的调用者cout << "this: " << this << endl;cout << "name: " << this->name << ", x: " << this->x;cout << ", y: " << this->y << ", z: " << this->z << endl;}void setName(const string& name) {this->name = name;  // 也可以区分同名变量}
};int main() {MyClass c1 = {"zs", 10, 3.14, 'M'};cout << "&c1: " << &c1 << endl;c1.disp();cout << "--------------" << endl;MyClass c2 = {"jack", 20, 2.4, 'F'};cout << "&c2: " << &c2 << endl;c2.disp();return 0;
}

10、类与结构体的区别 

C++增加了class类型后,仍保留了结构体类型(struct), 它不是简单地继承 C 的结构体,而是使它也具有类的特点,以便于用于面向对象程序设计。用struct声明的结构体类型实际上也就是类,两者区别如下:

  • struct的缺省作用域为public

  • class的缺省作用域为private

11、封装特性 

类背后蕴含的思想是数据抽象和封装,两个重要优点:

  • 避免类内部出现无意的、可能破坏对象状态的用户级错误
  • 随时间推移根据需求改变或缺陷报告来完善类实现,而不需要改变用户级代码

        C++通过类实现数据的封装,将相关的数据与对数据的操作封装到一个类中。

        类里面的数据和成员函数通过成员限定符的修饰,改变了这些成员的访问属性,实现了信息的隐蔽。

        在修改代码时,具体修改某个类,不会对别的类产生影响,方便调试检查错误,这对于大型项目尤为重要。

C++中,封装通常通过以下方式实现:

  • 使用 private protected 访问修饰符来限制对类成员的直接访问
  • 提供公共方法( getter setter )来间接访问或修改私有成员变量

案例:

class BankAccount {
private:double balance; // 私有成员变量,外部无法直接访问public:// 构造函数BankAccount(double initialBalance) : balance(initialBalance) {}// Getter 方法double getBalance() const {return balance;}// 存款方法void deposit(double amount) {if (amount > 0) {balance += amount;}}// 取款方法bool withdraw(double amount) {if (balance >= amount && amount > 0) {balance -= amount;return true;}return false;} // 修正:将分号移至此处
}; // 修正:类定义结束符

         在这个例子中, balance 变量是私有的,外界不能直接访问它。相反,提供了 getBalance , deposit withdraw 方法作为与 balance 交互的接口。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/90553.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/90553.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图片查重从设计到实现(4)图片向量化存储-Milvus 单机版部署

Milvus 单机版部署 在 Docker 环境下安装、应用和配置 Milvus 向量数据库可以按照以下步骤进行&#xff0c;涵盖从安装到基础应用的完整流程&#xff1a; 1. 部署前准备 服务器&#xff1a;建议测试环境配置 2 核 CPU、8GB 内存&#xff1b;处理 100 万组向量数据&#xff0c;…

前端版本更新检测机制

&#x1f4cc; 一、为什么需要前端版本更新检测机制&#xff1f;在现代 Web 项目中&#xff0c;我们通常会通过 CDN 或缓存策略来加快页面加载速度&#xff0c;但这也带来了一个问题&#xff1a;用户可能访问的是旧版本的页面或资源&#xff0c;而不会自动更新到最新版本。这在…

Python(09)正则表达式

特殊字符 1. 基本元字符 .&#xff1a;匹配除换行符以外的任意单个字符。 *&#xff1a;匹配前面的元素零次或多次。 &#xff1a;匹配前面的元素一次或多次。 ?&#xff1a;匹配前面的元素零次或一次。 2. 定量符 {n}&#xff1a;匹配前面的元素恰好 n 次。 {n,}&#xff1a;…

k8s容器放开锁内存限制

参考&#xff1a;https://access.redhat.com/solutions/1257953 问题 nccl-test容器docker.io/library/nccl-tests:24.12中跑mpirun&#xff0c;buff设置为NCCL_BUFFSIZE503316480 提示out of memory&#xff1a; pod-1:78:91 [0] include/alloc.h:114 NCCL WARN Cuda failure …

基于Zigee的温度数据采集系统

大家好&#xff0c;本文带来的是单片机课设-基于Zigee的温度数据采集系统。 一、设计内容和要求 基于Zigbee的数据采集系统 1.1设计内容 &#xff08;1&#xff09;分析对比Bluetooth、Zigbee、Lora方式组网的基本原理和性能差异&#xff0c;撰写分析报告&#xff1b; &#xf…

ATH12K 驱动框架分析

文章目录 Linux Wireless 驱动框架深入分析 **1. 核心框架层次结构** **1.1 cfg80211 子系统 (`net/wireless/`)** **1.2 mac80211 子系统 (`net/mac80211/`)** **2. ath12k 驱动架构分析** **2.1 核心管理文件** **2.2 数据路径文件** **2.3 平台接口文件** **2.4 功能模块文件…

OSPF路由协议单区域

RIP的不足 以跳数评估的路由并非最优路径 如果RTA选择S0/0传输&#xff0c;传输需时会大大缩短为3sRIP协议限制网络直径不能超过16跳 收敛速度慢 RIP定期路由更新 – 更新计时器&#xff1a;定期路由更新的时间间隔&#xff0c;默认30秒。 – 失效计时器&#xff1a;失效计时器…

Kubernetes部署与管理Scrapy爬虫:企业级分布式爬虫平台构建指南

引言&#xff1a;Kubernetes在爬虫领域的战略价值在大规模数据采集场景中&#xff0c;​​容器化爬虫管理​​已成为企业级解决方案的核心。根据2023年爬虫技术调查报告&#xff1a;采用Kubernetes的爬虫系统平均资源利用率提升​​65%​​故障恢复时间从小时级缩短至​​秒级​…

Web-Machine-N7靶机攻略

一.环境准备&#xff08;VBox&#xff0c;kali虚拟机&#xff0c;靶机&#xff09; 1.1Vbox下载地址: Downloads – Oracle VirtualBox 1.2将N7导入到这个虚拟机中 1.3将kali和Vbox都设置成桥接模式 1.4开启靶机 若鼠标出不来可以使用组合技,CtrlAltDelete强制退出 二.信息…

用毫秒级视频回传打造稳定操控闭环之远程平衡控制系统技术实践

在工业自动化、远程机器人、无人装备等复杂作业场景中&#xff0c;远程实时操控正逐步取代传统“监控指令”模式&#xff0c;成为提升效率与保障安全的关键能力。尤其在高风险、高精度的应用环境中&#xff0c;操作者不仅要“能控”&#xff0c;更要“看得准、反应快”。 真正…

瑞萨电子RA-T MCU系列新成员RA2T1——电机控制专家

RA2T1系列微控制器基于64MHz ArmCortex-M23内核设计&#xff0c;专为单电机控制应用而优化。RA2T1集成PWM定时器&#xff0c;以及配备3个采样保持电路的A/D转换器等先进的模拟功能&#xff0c;适用于电动工具&#xff0c;风扇和家用电器等高效的低端电机控制方案。RA2T1支持1.6…

Java排序算法之<选择排序>

目录 1、选择排序 1.1、介绍 1.2、稳定性 2、执行流程 3、java实现 4、优缺点 总结&#xff1a;Java 排序算法进阶路线 O(n) 算法&#xff08;适合学习原理&#xff09; 冒泡排序&#xff08;最慢&#xff09;→ 选择排序 → 插入排序&#xff08;推荐先学&#xff09; …

ESP8266 http收发数据

1.先修改基础配置 make menuconfig 打开配置菜单 选择component config 然后选择 修改波特率为115200 保存退出 2.修改彩色日志打印的 在component config目录下找到log output 选中点击空格关掉彩色日志输出&#xff0c;这样正常串口打印就没有乱码了 然后保存退出 3…

ZLMediaKit 源代码入门

ZLMediaKit 是一个基于 C11 开发的高性能流媒体服务器框架&#xff0c;支持 RTSP、RTMP、HLS、HTTP-FLV 等协议。以下是源代码入门的详细指南&#xff1a; 1. 源码结构概览 主要目录结构&#xff1a; text ZLMediaKit/ ├── cmake/ # CMake 构建配置 ├── …

智能Agent场景实战指南 Day 21:Agent自主学习与改进机制

【智能Agent场景实战指南 Day 21】Agent自主学习与改进机制 文章内容 开篇 欢迎来到"智能Agent场景实战指南"系列的第21天&#xff01;今天我们将深入探讨智能Agent的自主学习与改进机制——这是使Agent能够持续提升性能、适应动态环境的核心能力。在真实业务场景…

微信小程序中英文切换miniprogram-i18n-plus

原生微信小程序使用 miniprogram-i18n-plus第一步&#xff1a;npm install miniprogram-i18n-plus -S安装完成后&#xff0c;会在项目文件文件夹 node_modules文件里生成 miniprogram-i18n-plus&#xff0c; 然后在工具栏-工具-构建npm&#xff0c;然后看到miniprogram_npm里面…

LeetCode 127:单词接龙

LeetCode 127&#xff1a;单词接龙问题本质&#xff1a;最短转换序列的长度 给定两个单词 beginWord 和 endWord&#xff0c;以及字典 wordList&#xff0c;要求找到从 beginWord 到 endWord 的最短转换序列&#xff08;每次转换仅改变一个字母&#xff0c;且中间单词必须在 wo…

docker搭建ray集群

1. 安装docker 已安装过docker 没安装流程 启动 Docker 服务&#xff1a; sudo systemctl start docker sudo systemctl enable docker # 设置开机即启动docker验证 Docker 是否安装成功&#xff1a; docker --version2. 部署ray # 先停止docker服务 systemctl stop docker…

【iOS】SideTable

文章目录前言1️⃣Side Table 的核心作用&#xff1a;扩展对象元数据存储1.1 传统对象的内存限制1.2 Side Table 的定位&#xff1a;集中式元数据仓库2️⃣Side Table 的底层结构与关联2.1 Side Table 与 isa 指针的关系2.2 Side Table 的存储结构2.3 SideTable 的工作流程3️⃣…

【Spring Cloud Gateway 实战系列】高级篇:服务网格集成、安全增强与全链路压测

一、服务网格集成&#xff1a;Gateway与Istio的协同作战在微服务架构向服务网格演进的过程中&#xff0c;Spring Cloud Gateway可与Istio形成互补——Gateway负责南北向流量&#xff08;客户端到集群&#xff09;的入口管理&#xff0c;Istio负责东西向流量&#xff08;集群内服…