C++ | 面向对象 | 类

👻类

👾语法格式

class className{Access specifiers:				// 访问权限DataType variable;			// 变量returnType functions() { }	// 方法
};

👾访问权限

class className {public:// 公有成员protected:// 受保护成员private:// 私有成员
};

👽公有成员 public

公有成员在类外部可访问,可不使用任何成员函数来设置和获取公有变量的值

class Line {public:double length;void setLength(double len);double getLength(void);
};
double Line::getLength(void) { return length ; }
void Line::setLength(double len) { length = len; }...
Line line;
// 使用成员变量(正确,因为 length 公有) 
line.length = 10.0;
cout << line.length <<endl;
// 使用成员函数
line.setLength(6.0);
cout << line.getLength() <<endl;

👽私有成员 private

私有成员在类外部不可访问,不可查看,只有友元函数可以访问私有成员。没有使用任何访问修饰符默认私有。

class Box {public:double length;void setWidth(double wid);double getWidth(void);private:double width;
};
double Box::getWidth(void) { return width; }
void Box::setWidth( double wid ) { width = wid; }...
Box box;
// box.width = 10.0; 	// [Error] 'double Box::width' is private
box.setWidth(10.0);  	// 使用成员函数设置宽度
cout << box.getWidth() <<endl;

👽受保护成员 protected

受保护成员与私有成员相似,但有一点不同,受保护成员在**派生类(即子类)**中是可访问的。

/* 基类 */ 
class Box {protected:double width;
};/* 派生类 */
class SmallBox: Box {  public:void setSmallWidth(double wid);double getSmallWidth();
};
double SmallBox::getSmallWidth() { return width; }
void SmallBox::setSmallWidth(double wid) { width = wid; }...
SmallBox box;
box.setSmallWidth(5.0);
cout << box.getSmallWidth() << endl;

👻类成员函数

👾语法格式

  • 定义在类定义内部

    class className{returnType functions(parameter list) { ... }
    };
    
  • 单独使用范围解析运算符 :: 定义

    class className{returnType functions(parameter list);
    };
    returnType class_name::functions(parameter list){ ... }
    

👾示例代码

  • 定义在类定义内部

    class Box {public:double length;  // 长度double breadth; // 宽度double height;  // 高度double getVolume() { return length * breadth * height; }
    };
    
  • 单独使用范围解析运算符 :: 定义

    class Box {public:double length;  // 长度double breadth; // 宽度double height;  // 高度double getVolume();
    };double Box::getVolume() { return length * breadth * height; }
    

👻类构造函数

  • 类构造函数在每次创建类的新对象时执行。
  • 构造函数的名称与类的名称是完全相同的,不返回任何类型。
  • 构造函数可用于为某些成员变量设置初始值

👾无参构造函数

👽语法格式

class className{className() { ... }
};

👽示例代码

class Line {public:Line() { cout << "Object is being created" << endl; }private:double length;
};...
Line line;	// 输出:Object is being created

👾带参构造函数

👽语法格式

class className{className(parameter list) { ... }
};

👽示例代码

class Line {public:Line(double len) {length = len;cout << "Object is being created" << endl;}private:double length;
};...
Line line;	// 输出:Object is being created

👾初始化列表构造函数

👽语法格式

类有多个字段 A、B、C 等需要进行初始化:

class className{className(int a,int b,int c): A(a), B(b), C(c) { ... }
};

👽示例代码

class Line {public:Line(double len): length(len) { cout << "Object is being created" << endl; }private:double length;
};...
Line line;	// 输出:Object is being created

等同语法:

Line(double len) {length = len;cout << "Object is being created" << endl;
}

注意:初始化类成员时,是按声明的顺序初始化,而不是按初始化列表的顺序初始化,即:

class Base {public:// 按照 A -> B -> C 的顺序初始化int A;int B;int C;// 而不是按照 C -> B -> A 的顺序初始化Base(int a, int b, int c): C(c), B(b), A(a) {}
};

👻类析构函数

  • 类析构函数在每次删除所创建的对象时执行。
  • 析构函数的名称与类的名称是完全相同的,在前面加 波浪号(~) 作为前缀,不返回任何值,不能带有任何参数。
  • 析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源

👾语法格式

类有多个字段 A、B、C 等需要进行初始化:

class className{~className() { ... }
};

👾示例代码

class Line {public:Line() { cout << "Object created" << endl; }  // 构造函数声明~Line() { cout << "Object deleted" << endl; } // 析构函数声明
};...
Line line;

一个类内可以有多个构造函数(相当于重载构造函数),只有一个析构函数

class Matrix {public:Matrix(int row, int col);       //普通构造函数Matrix(const Matrix& matrix);   //拷贝构造函数Matrix();                       //构造空矩阵的构造函数~Matrix();
};

👻类拷贝构造函数

👾语法结构

class className{// obj是对象引用,用于初始化另一个对象className (const className &obj) { }
};
  • 参数 obj —— 本类的引用

    • const 类型引用 —— 既能以常量对象(初始化后值不改变的对象)作为参数,也能以非常量对象作为参数初始化其他对象

    • const 类型引用 —— 一般不使用

👾被调用情况

  • 情况1:用一个对象初始化同类的另一个对象时,调用被初始化的对象的拷贝构造函数

    Base a2(a1);	
    Base a2 = a1;
    
    class Base {public:int num;// 一般构造函数Base(int n) {num = n;cout << "General Constructor called" << endl;}// 拷贝构造函数Base(const Base& a) {num = a.num;cout << "Copy Constructor called" << endl;}
    };...// 只调用a1的一般构造函数,输出 General Constructor called
    Base a1(1);	
    // 只调用a2的拷贝构造函数,输出 Copy Constructor called
    Base a2(a1);	
    Base a2 = a1;
    // 不调用
    a2 = a1;		
    

    注意,Base a2 = a1; 是初始化语句,不是赋值语句。

    赋值语句的等号左边是一个早已有定义的变量,不会引发复制构造函数的调用,

    Base a1(1);	// 调用a1的一般构造函数
    Base a2(a1);	// 调用a2的拷贝构造函数
    a2 = a1;		// 不调用,因为 a2 早已生成且初始化,不会引发拷贝构造函数
    

    只会调用被初始化的对象的拷贝构造函数,不会调用其一般构造函数

  • 情况2:函数参数是类的对象,当函数调用时,调用对象的拷贝构造函数

    class Base {public:// 一般构造函数Base() { cout << "General Constructor called" << endl; };// 拷贝构造函数Base(Base& a) { cout << "Copy constructor called" << endl; }
    };
    void Function(Base a) {}...Base a;		// 调用a的一般构造函数,输出 General Constructor called
    Function(a);	// 调用a的拷贝构造函数,输出 Copy constructor called
    
    • 对象作为形参,函数被调用时,生成形参要用拷贝构造函数,会带来时间开销
    • 对象的引用作为形参,无时间开销,但有一定的风险,因为如果形参的值发生改变,实参值也会改变

    如果要确保实参值不改变,又希望避免拷贝构造函数的开销,解决办法是将形参声明为对象的 const 引用:

    void Function(const Base &a){...
    }
    
  • 情况3:函数返回值是类的对象,当函数返回时,调用对象的拷贝构造函数

    class Base {public:int num;// 一般构造函数Base(int n) {num = n;cout << "General Constructor called" << endl;}// 拷贝构造函数Base(const Base& a) {num = a.num;cout << "Copy constructor called" << endl;}
    };Base Func() {Base a(4);	// 调用一般构造函数,输出 General Constructor calledreturn a;
    }...int a = Func().num;	// 调用拷贝构造函数,输出 Copy constructor called
    

    有些编译器出于程序执行效率的考虑,编译时进行优化,函数返回值对象不用拷贝构造函数,不符合C++标准

👾深拷贝和浅拷贝

👽浅拷贝

  • 使用情况:基本类型数据、简单对象
  • 原理:按位复制内存
// 基本类型数据
int a = 10;
int b = a;
// 简单对象
class Base {public:Base(): m_a(0), m_b(0) { }Base(int a, int b): m_a(a), m_b(b) { }private:int m_a;int m_b;
};Base obj1(10, 20);
Base obj2 = obj1;

👽深拷贝

  • 使用情况
    • 持有其它资源的类(如动态分配的内存、指向其他数据的指针)
    • 标准模板库(STL)中 string、vector、stack 等
    • 创建对象时进行一些预处理工作,比如统计创建过的对象的数目、记录对象创建的时间等
  • 原理:显式定义拷贝构造函数
  • 过程
    • 会将原有对象的所有成员变量拷贝给新对象
    • 会为新对象再分配一块内存,并将原有对象所持有的内存也拷贝过来
  • 结果:原有对象和新对象所持有的动态内存相互独立,更改一个对象的数据不影响另一个对象
// 持有其它资源的类(如动态分配的内存、指向其他数据的指针)
#include <cstdlib>
#include <iostream>
using namespace std;class Array {public:Array(const int len) {m_len = len;m_p = (int*)calloc(m_len, sizeof(int));}Array(const Array& arr) {this->m_len = arr.m_len;this->m_p = (int*)calloc(this->m_len, sizeof(int));memcpy(this->m_p, arr.m_p, m_len * sizeof(int));}~Array() { free(m_p); }int operator[](int i) const { return m_p[i]; }int& operator[](int i) { return m_p[i]; }int length() const { return m_len; }private:int m_len;int* m_p;
};void printArray(const Array& arr) {int len = arr.length();for(int i = 0; i < len; i++) { if(i == len - 1) { cout << arr[i] << endl; } else { cout << arr[i] << ", "; } }
}...int main() {Array arr1(10);for(int i = 0; i < 10; i++)arr1[i] = i;Array arr2 = arr1;arr2[5] = 100;arr2[3] = 29;printArray(arr1); // 输出:0, 1, 2, 3, 4, 5, 6, 7, 8, 9printArray(arr2); // 输出:0, 1, 2, 29, 4, 100, 6, 7, 8, 9
}
// 创建对象时进行一些预处理工作
#include <iostream>
#include <ctime>
#include <windows.h>
using namespace std;class Base {public:Base(int a = 0, int b = 0) {m_a = a;m_b = b;m_count++;m_time = time(nullptr);}Base(const Base& obj) {this->m_a = obj.m_a;this->m_b = obj.m_b;this->m_count++;this->m_time = time(nullptr);}static int getCount() { return m_count; }time_t getTime() const { return m_time; }private:int m_a;int m_b;time_t m_time;      //对象创建时间static int m_count; //创建过的对象的数目
};int Base::m_count = 0;...int main() {Base obj1(10, 20);cout << "obj1: count = " << obj1.getCount() << ", time = " << obj1.getTime() << endl;// 输出:obj1: count = 1, time = 1740055359Sleep(3000);Base obj2 = obj1;cout << "obj2: count = " << obj2.getCount() << ", time = " << obj2.getTime() << endl;// 输出:obj2: count = 2, time = 1740055362return 0;
}

👻类静态成员

👾类静态成员变量

👽声明

使用 关键字 static 把类成员声明为静态

class Box {public:static int objectCount; // 声明静态变量Box() { objectCount++; } // 每次创建对象时调用构造函数,静态变量值加 1
};

无论创建多少个类的对象,静态成员只有一个副本,在类的所有对象中是共享的。

👽定义

在类外部通过 范围解析运算符 :: 定义静态变量

class Box {public:static int objectCount; // 声明静态变量Box() { objectCount++; } 
};
// 仅定义却不初始化 类静态成员
int Box::objectCount;

无论是否初始化,必须定义,否则报错:

(.rdata$.refptr._ZN3Box11objectCountE[.refptr._ZN3Box11objectCountE]+0x0): undefined reference to `Box::objectCount'

👽初始化

// 定义+初始化 类静态成员
int Box::objectCount = 0;

若不初始化,则在创建第一个对象时所有静态数据初始化为零

👽访问

  • 使用 范围解析运算符 :: 访问类静态变量
Box box1();    // 声明 box1
Box box2();    // 声明 box2
cout << "创建对象总数: " << Box::objectCount << endl;

👾类静态成员函数

👽声明定义

  • 使用 关键字 static 把类成员声明为静态
class Box {public:static int objectCount; // 声明静态变量Box() { objectCount++; }static int getCount() { // 声明定义静态函数return objectCount;}
};
  1. 普通成员函数有 this 指针,可以访问类中的任意成员
  2. 静态成员函数没有 this 指针,只能访问静态成员变量其他静态成员函数类外部的其他函数

👽使用

  • 使用 范围解析运算符 :: 访问类静态变量
cout << "创建对象总数: " << Box::getCount() << endl; // 对象不存在时 也能被调用
Box box1();    // 声明 box1
Box box2();    // 声明 box2
cout << "创建对象总数: " << Box::getCount() << endl;

静态成员函数即使 类对象不存在 也能被调用


👻友元

👾友元函数

  • 类的友元函数定义在类外部,但有权访问类的私有成员、保护成员
  • 尽管友元函数的原型在类的定义中出现,但友元函数不是成员函数

👽声明

使用 关键字 friend 把函数声明为友元函数

class Box {double width;public:Box(double w): width(w) {}friend void printWidth(Box box);  // 在函数前使用关键字 friend
};

👽定义

class Box {double width;public:Box(double w): width(w) {}friend void printWidth(Box box);
};/* printWidth() 不是任何类的成员函数,但因为它是 Box 的友元,可直接访问该类的任何成员 */
void printWidth(Box box) { cout << "Width of box : " << box.width <<endl;}

👽使用

Box box;
box.setWidth(10.0);
printWidth(box); 	// 使用友元函数访问Box中的私有成员

👾友元类

👽声明定义

class Box {public:Box(double w): width(w) {}friend class BigBox;private:double width;
};/* BigBox是Box的友元类,它可以直接访问Box类的任何成员 */
class BigBox {public :void printWidth(Box& box) { cout << "Width of box : " << box.width << endl; }
};

👽使用

Box box(10.0);
BigBox big;
big.printWidth(box); // 使用友元类中的方法访问Box中的私有成员

👻 this 指针

  • this 指针是一个特殊的指针,它指向当前对象的实例,每一个对象都能通过 this 指针来访问自己的地址。
  • 当一个对象的成员函数被调用时,编译器会隐式地传递该对象的地址作为 this 指针。
  • 友元函数没有 this 指针,因为友元不是类的成员
class MyClass {private:int value;public:// 可以明确地告诉编译器想访问当前对象的成员变量,而不是函数参数或局部变量,避免命名冲突void setValue(int value) { this->value = value; } void getValue() { return this->value; }
};...MyClass obj;
obj.setValue(42);
int value = obj.getValue();

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

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

相关文章

深入了解 Python 中的 MRO(方法解析顺序)

文章目录 深入了解 Python 中的 MRO&#xff08;方法解析顺序&#xff09;什么是 MRO&#xff1f;如何计算 MRO&#xff1f;C3 算法的合并规则C3 算法的合并步骤示例&#xff1a;合并过程解析 MRO 解析失败的场景使用 mro() 方法查看 MRO示例 1&#xff1a;基本用法 菱形继承与…

信息系统的安全防护

文章目录 引言**1. 物理安全****2. 网络安全****3. 数据安全****4. 身份认证与访问控制****5. 应用安全****6. 日志与监控****7. 人员与管理制度****8. 其他安全措施****9. 安全防护框架**引言 从技术、管理和人员三个方面综合考虑,构建多层次、多维度的安全防护体系。 信息…

Tailwind CSS 4【实用教程】

官网 https://tailwindcss.com/docs/installation/using-vite Tailwind CSS 是一个实用优先的 CSS 框架 特色 原子化样式类名可深度定制主题插件丰富 安装配置导入 vite 中 pnpm add tailwindcss tailwindcss/vitevite.config.ts 中配置 import tailwindcss from tailwindcs…

ChatGPT 提示词框架

作为一个资深安卓开发工程师&#xff0c;我们在日常开发中经常会用到 ChatGPT 来提升开发效率&#xff0c;比如代码优化、bug 排查、生成单元测试等。 但要想真正发挥 ChatGPT 的潜力&#xff0c;我们需要掌握一些提示词&#xff08;Prompt&#xff09;的编写技巧&#xff0c;并…

毕业项目推荐:基于yolov8/yolo11的苹果叶片病害检测识别系统(python+卷积神经网络)

文章目录 概要一、整体资源介绍技术要点功能展示&#xff1a;功能1 支持单张图片识别功能2 支持遍历文件夹识别功能3 支持识别视频文件功能4 支持摄像头识别功能5 支持结果文件导出&#xff08;xls格式&#xff09;功能6 支持切换检测到的目标查看 二、数据集三、算法介绍1. YO…

【Python 入门基础】—— 人工智能“超级引擎”,AI界的“瑞士军刀”,

欢迎来到ZyyOvO的博客✨&#xff0c;一个关于探索技术的角落&#xff0c;记录学习的点滴&#x1f4d6;&#xff0c;分享实用的技巧&#x1f6e0;️&#xff0c;偶尔还有一些奇思妙想&#x1f4a1; 本文由ZyyOvO原创✍️&#xff0c;感谢支持❤️&#xff01;请尊重原创&#x1…

VM虚拟机安装与配置Ubuntu Linux操作系统详细教程~

一、下载VM虚拟机 VMware16.0.zip百度网盘下载链接:https://pan.baidu.com/s/1-l-CcAVNINqhRLSiQ26R7w?pwd=tznn 提取码: tznn 二、软件介绍 VMware(虚拟机)是指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统,通过它可在一台电脑上同…

LabVIEW同步数据采集功能

VI通过使用数据采集&#xff08;DAQ&#xff09;硬件系统&#xff0c;进行多通道同步采集&#xff0c;实时获取模拟信号数据。它利用外部时钟信号触发数据采集&#xff0c;支持连续采样模式&#xff0c;并将采集到的数据实时显示在波形图上&#xff0c;方便用户进行数据监控和分…

释放 Cursor 的全部潜能:快速生成智能 Cursor Rules

释放 Cursor 的全部潜能&#xff1a;使用 PromptCoder 从 package.json 快速生成智能 Cursor Rules 我们将深入探讨如何利用您项目中的 package.json 文件&#xff0c;轻松生成 Cursor Rules&#xff0c;并通过 PromptCoder 这个强大的工具&#xff0c;快速创建高质量的 curso…

基于LangChain4j调用火山引擎DeepSeek R1搭建RAG知识库实战指南

基于LangChain4j调用火山引擎DeepSeek R1搭建RAG知识库实战指南 基于LangChain4j调用火山引擎DeepSeek R1搭建RAG知识库实战指南 基于LangChain4j调用火山引擎DeepSeek R1搭建RAG知识库实战指南一、注册火山引擎账号二、RAG技术核心原理三、环境与工具准备1. 核心组件2. 依赖配…

虚拟仿真无线路由器5G和2.4G发射信号辐射对比(虚拟仿真得出最小安全距离,与国际标准要求一致)

1、前言 有人说&#xff0c;只要有电磁波的地方就有辐射。5G和2.4G信号辐射强度是多少&#xff1f;是否会对人体构成危害&#xff1f;无线路由器的2.4GHz频段&#xff0c;频率范围&#xff1a;2.4 GHz 至 2.4835 GHz&#xff0c;信道宽度&#xff1a;通常为20 MHz&#xff0c;…

苍穹外卖-阿里云OSS文件上传

苍穹外卖-阿里云OSS文件上传 一、阿里云OSS简介**获取AccessKey**获取enpoint 二、代码实现1 引入依赖2 定义OSS相关配置2.1 application-dev.yml2.2 application.yml 3 读取OSS配置3.1 AliOssProperties 4 生成OSS工具类对象4.1 AliOssUtil4.2 OssConfiguration2.5 CommonCont…

一劳永逸解决vsocde模块import引用问题

这里写目录标题 原因解决方案 原因解决方案 原因&#xff1a; VSCode中需要显式地声明PYTHONPATH&#xff0c;不然根本找不到本项目内的模块和包的路径。 解决方法&#xff0c;加入到setting。json里当前Project路径&#xff0c;以后运行就自动添加了&#xff1a; 打开设置 …

Xlua 编译 Windows、UWP、Android、iOS 平台支持库

Xlua 编译 Windows、UWP、Android、iOS 平台支持库 Windows&#xff1a; 安装 Visual Studio&#xff08;推荐 2017 或更高版本&#xff09; 安装 CMake&#xff08;https://cmake.org/&#xff09; macOS&#xff1a; 安装 Xcode 和命令行工具 安装 CMake 检查 cmake 是否安…

JWT使用教程

目录 JWT (JSON Web Token)1. JWT简介(1) 什么是JWT(2) JWT有什么用(3) JWT认证方式 2. JWT的组成部分3. 签名的目的4. JWT与Token的区别5 JWT的优势6 JJWT签发与验证token(1) 引入依赖(2) 创建 Token(3) 解析Token(4) 设置过期时间(5) 自定义claims 7. JWT自定义工具类 JWT (J…

如何使用Docker搭建哪吒监控面板程序

哪吒监控(Nezha Monitoring)是一款自托管、轻量级的服务器和网站监控及运维工具,旨在为用户提供实时性能监控、故障告警及自动化运维能力。 文档地址:https://nezha.wiki/ 本章教程,使用Docker方式安装哪吒监控面板,在此之前,你需要提前安装好Docker. 我当前使用的操作系…

时间无关和时间相关的N-S方程

注意区分&#xff1a; 时间无关的情况&#xff08;稳态Navier-Stokes方程&#xff09;和时间相关的情况&#xff08;非定常Navier-Stokes方程&#xff09;。前者与时间无关&#xff0c;后者与事件有关。比如爆轰案例就是与时间相关的情况&#xff08;非定常Navier-Stokes方程&…

APISIX Dashboard上的配置操作

文章目录 登录配置路由配置消费者创建后端服务项目配置上游再创建一个路由测试 登录 http://192.168.10.101:9000/user/login?redirect%2Fdashboard 根据docker 容器里的指定端口&#xff1a; 配置路由 通过apisix 的API管理接口来创建&#xff08;此路由&#xff0c;直接…

【Mac电脑本地部署Deepseek-r1:详细教程与Openwebui配置指南】

文章目录 前言电脑配置&#xff1a;安装的Deepseek版本&#xff1a;使用的UI框架&#xff1a;体验效果展示&#xff1a;本地部署体验总结 部署过程Ollama部署拉取模型运行模型Openwebui部署运行Ollama服务在Openwebui中配置ollama的服务 后话 前言 deepseek最近火的一塌糊涂&a…

老旧android项目编译指南(持续更)

原因 编译了很多项目&#xff0c;找到了一些可观的解决办法 1. android studio里面的jdk版本切换 jdk版本切换在这里&#xff0c;一般安卓开发需要用到4个版本的jdk,jdk8, jdk11, jdk17, jdk21新版的android stuio是默认使用高版本的jdk,所以切换版本是很有必要的 2. 命令…