【C++篇】类与对象的秘密(上)

目录

引言

一、类的定义

1.1类定义的基本格式

1.2 成员命名规范

1.3 class与struct的区别

1.4 访问限定符

1.5 类的作用域

二、实例化

2.1 类的实例化

2.2 对象的大小与内存对齐

三、this 指针

3.1 this指针的基本用法

3.2 为什么需要this指针?

3.3 this指针的限制

四、C++和C语言实现Stack的对比

4.1 C语言实现Stack

4.2 C++语言实现Stack

4.3 C++与C实现的对比总结

五、总结


引言

在C++中,类和对象是面向对象编程的基础组成部分。通过类,程序员可以对现实世界的实体进行模拟和抽象。类的基本概念包括成员变量、成员函数、访问控制等。本篇博客将介绍C++类与对象的基础知识,为后续学习打下良好的基础。

一、类的定义

在C++中,类通过将数据和行为封装在一起,模拟现实世界中的对象。类的定义通常包含成员变量(描述对象的状态)和成员函数(定义对象的行为)。类的定义使用class关键字,并以分号结束。

1.1类定义的基本格式

类的定义格式如下所示:

#include <iostream>
using namespace std;
​
class Stack {
public:// 初始化栈void Init(int n = 4) {array = new int[n];capacity = n;top = 0;}
​// 将元素推入栈void Push(int x) {array[top++] = x;}
​// 获取栈顶元素int Top() {if (top > 0) {return array[top - 1];}return -1; // 栈为空时返回-1}
​// 销毁栈void Destroy() {delete[] array;array = nullptr;top = capacity = 0;}
​
private:int* array;      // 栈数据数组size_t capacity; // 栈容量size_t top;      // 栈顶指针
};
​
int main() {Stack st;st.Init();st.Push(1);st.Push(2);cout << st.Top() << endl;st.Destroy();return 0;
}

在上述代码中:

  • class关键字用于定义类,Stack是类的名称。

  • public修饰的成员函数可以在类的外部访问,例如InitPushTopDestroy

  • private修饰的成员变量(如arraycapacitytop)只能在类的内部访问,无法在类外部直接使用。

1.2 成员命名规范

在C++中,通常会为类的成员变量使用特定的命名约定,以避免与函数参数或局部变量混淆。这些命名约定可以提高代码的可读性和维护性。例如:

  • 使用下划线前缀:如_year

  • 使用m_前缀:如m_month

  • 使用驼峰命名法:如dayOfMonth

1.3 class与struct的区别

C++中的classstruct的主要区别在于默认的访问权限:

  • class中,未标明的成员变量和成员函数默认是private

    struct ExampleClass {int a; // 默认 private
    };
  • struct中,未标明的成员变量和成员函数默认是public

    struct ExampleStruct {int a; // 默认 public
    };

1.4 访问限定符

访问限定符用于控制类的成员的可见性。C++支持三种访问限定符:

  • public:公共成员可以在类的外部访问。

  • private:私有成员只能在类的内部访问。

  • protected:保护成员只能在类内部或派生类中访问(会在继承中详细讲解)。

 

访问限定符从出现的位置开始生效,直到遇到下一个访问限定符或类定义结束为止。例如:

class Date {
public:void Init(int year, int month, int day) {_year = year;_month = month;_day = day;}
​
private:int _year;int _month;int _day;
};
​
int main() {Date d;d.Init(2024, 3, 31);// 无法直接访问 _year, _month, _day,因为它们是私有成员return 0;
}

在上述示例中,Init函数是公共的,可以在类外部调用;而_year_month_day是私有的,只能通过成员函数访问。

1.5 类的作用域

类的作用域决定了类成员的可访问性。当在类的外部定义成员函数时,需要使用作用域解析符::来指明成员函数所属的类。

#include <iostream>
using namespace std;
​
class Stack {
public:void Init(int n = 4);
private:int* array;size_t capacity;size_t top;
};
​
// 在类外定义成员函数
void Stack::Init(int n) {array = new int[n];capacity = n;top = 0;
}
​
int main() {Stack st;st.Init();return 0;
}

通过使用Stack::Init,编译器可以知道Init函数属于Stack类,并能在类的作用域中查找成员变量arraycapacitytop

二、实例化

2.1 类的实例化

实例化是指在物理内存中创建对象的过程。类提供了对象的结构和行为,但本身不占用物理空间,只有实例化后才会在内存中分配空间。

⼀个类可以实例化出多个对象 ,实例化出的对象 占⽤实际的物理空间,存储类成员变量。打个⽐⽅:类实例化出对象就像现实中使⽤建筑设计图建造出房⼦,类就像是设计图,设计图规划了有多少个房间,房间⼤⼩功能等,但是并没有实体的建筑存在,也不能住⼈,⽤设计图修建出房⼦,房⼦才能住⼈。同样类就像设计图⼀样,不能存储数据,实例化出的对象分配物理内存存储数据。
#include <iostream>
using namespace std;
​
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;d1.Init(2024, 3, 31);d1.Print();return 0;
}

在上述代码中,Date d1实例化了一个Date对象,并调用了InitPrint成员函数。

2.2 对象的大小与内存对齐

对象的大小由成员变量决定,成员函数不影响对象的大小。

类实例化出的每个对象,都有独⽴的数据空间,所以对象中肯定包含成员变量,那么成员函数是否包含呢?⾸先函数被编译后是⼀段指令,对象中没办法存储,这些指令存储在⼀个单独的区域(代码段),那么对象中⾮要存储的话,只能是成员函数的指针。再分析⼀下,对
象中是否有存储指针的必要呢,Date实例化d1和d2两个对象,d1和d2都有各⾃独⽴的成员变量_year/_month/_day存储各⾃的数据,但是d1和d2的成员函数Init/Print指针却是⼀样的,存储在对象中就浪费了。如果⽤Date实例化100个对象,那么成员函数指针就重复存储100次,太浪费了。这⾥需要再额外哆嗦⼀下, 其实函数指针是不需要存储的,函数指针是⼀个地址,调⽤函数被编译成汇编指令[call 地址], 其实编译器在编译链接时,就要找到函数的地址,不是在运⾏时找 ,只有动态多态是在运⾏时找,就需要存储函数地址,这个我们以后会讲解。

C++规定类的对象也需要符合内存对齐的规则,以提高访问效率。

#include <iostream>
using namespace std;
​
class A {
private:char _ch; // 1 字节int _i;   // 4 字节
};
​
int main() {A a;cout << sizeof(a) << endl; // 输出8字节,因内存对齐return 0;
}

虽然_ch_i占用5字节,但由于内存对齐,实际大小为8字节。这样可以优化内存访问的性能。

结构体对齐详细介绍可参考我的另一篇博客 。

三、this 指针

this指针是C++中的一个隐含指针,指向调用成员函数的当前对象。它存在于每一个非静态成员函数中,用于区分成员变量和函数参数。当成员函数被调用时,this指针会自动传递给函数,使其能够访问调用该函数的对象的成员。

3.1 this指针的基本用法

在成员函数中,this指针用于访问当前对象的成员变量。例如:

class Date {
public:void Init(int year, int month, int day) {this->_year = year; // 使用 this 指针this->_month = month;this->_day = day;}
​void Print() {cout << this->_year << "/" << this->_month << "/" << this->_day << endl;}
​
private:int _year;int _month;int _day;
};
​
int main() {Date d1;d1.Init(2024, 3, 31);d1.Print();return 0;
}

在上述代码中,this->_year = year将参数year的值赋给当前对象的_year成员变量。this指针指向调用Init函数的对象(即d1),使得函数能够正确地操作对象的数据

3.2 为什么需要this指针?

this指针在以下情况下特别有用:

  1. 当成员变量和函数参数同名时,使用this可以避免命名冲突。

  2. 在链式调用中,返回*this可以实现对同一对象的连续操作。

class Person {
public:Person& SetName(const string& name) {this->name = name;return *this;}
​Person& SetAge(int age) {this->age = age;return *this;}
​void Display() {cout << "Name: "
​<< name << ", Age: " << age << endl;}
​
private:string name;int age;
};
​
int main() {Person p;p.SetName("Alice").SetAge(30).Display();return 0;
}

在上述示例中,SetNameSetAge函数返回*this,使得可以进行链式调用,即p.SetName("Alice").SetAge(30).Display()

3.3 this指针的限制

this指针是只读的,无法修改其指向。此外,在静态成员函数中无法使用this指针,因为静态成员函数不与任何对象关联。

四、C++和C语言实现Stack的对比

C++和C的区别不仅仅在于语法,而是在编程思想上的转变。C++是面向对象的编程语言,其三大特性为封装、继承和多态。在本节中,我们将通过对比C和C++两种语言的Stack实现来初步了解封装特性的优势。

4.1 C语言实现Stack

在C语言中,Stack的实现需要使用struct来定义栈的数据结构,并通过一系列函数来操作栈。数据和函数是分开的,操作时需要手动传递结构体指针来访问数据。

C语言Stack的代码示例

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>typedef int STDataType;typedef struct Stack {STDataType* a;int top;int capacity;
} ST;// 初始化栈
void STInit(ST* ps) {assert(ps);ps->a = NULL;ps->top = 0;ps->capacity = 0;
}// 销毁栈
void STDestroy(ST* ps) {assert(ps);free(ps->a);ps->a = NULL;ps->top = ps->capacity = 0;
}// 入栈
void STPush(ST* ps, STDataType x) {assert(ps);// 栈满时扩容if (ps->top == ps->capacity) {int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));if (tmp == NULL) {perror("realloc fail");return;}ps->a = tmp;ps->capacity = newcapacity;}ps->a[ps->top] = x;ps->top++;
}// 检查栈是否为空
bool STEmpty(ST* ps) {assert(ps);return ps->top == 0;
}// 出栈
void STPop(ST* ps) {assert(ps);assert(!STEmpty(ps));ps->top--;
}// 获取栈顶元素
STDataType STTop(ST* ps) {assert(ps);assert(!STEmpty(ps));return ps->a[ps->top - 1];
}// 获取栈的大小
int STSize(ST* ps) {assert(ps);return ps->top;
}int main() {ST s;STInit(&s);STPush(&s, 1);STPush(&s, 2);STPush(&s, 3);STPush(&s, 4);while (!STEmpty(&s)) {printf("%d\n", STTop(&s));STPop(&s);}STDestroy(&s);return 0;
}

C语言实现的特点

1.数据与操作分离:数据和操作函数是分开的,需要通过传递结构体指针来操作数据。

2.手动内存管理:程序员需要显式地进行内存分配和释放(使用mallocreallocfree)。

3.没有封装性:所有数据都是公开的,容易被随意修改,缺乏保护机制。

4.2 C++语言实现Stack

在C++中,可以利用类的封装特性将数据和操作结合在一起,使得栈的实现更为简洁和安全。C++通过构造函数和析构函数自动管理内存,无需手动初始化和销毁栈。

C++实现Stack的代码示例

#include <iostream>
#include <cassert>
using namespace std;typedef int STDataType;class Stack {
public:// 构造函数:初始化栈Stack(int n = 4) {_a = new STDataType[n];_capacity = n;_top = 0;}// 析构函数:释放内存~Stack() {delete[] _a;_a = nullptr;}// 入栈void Push(STDataType x) {if (_top == _capacity) {Expand(); // 栈满时扩容}_a[_top++] = x;}// 出栈void Pop() {assert(_top > 0); // 保证栈不为空--_top;}// 获取栈顶元素STDataType Top() const {assert(_top > 0);return _a[_top - 1];}// 检查栈是否为空bool Empty() const {return _top == 0;}// 获取栈的大小size_t Size() const {return _top;}private:STDataType* _a;      // 动态数组存储栈元素size_t _capacity;    // 栈的容量size_t _top;         // 栈顶指针// 辅助函数:扩容栈void Expand() {size_t newCapacity = _capacity * 2;STDataType* newArray = new STDataType[newCapacity];for (size_t i = 0; i < _top; ++i) {newArray[i] = _a[i];}delete[] _a;_a = newArray;_capacity = newCapacity;}
};int main() {Stack s;s.Push(1);s.Push(2);s.Push(3);s.Push(4);cout << "栈顶元素: " << s.Top() << endl; // 输出4s.Pop();cout << "栈顶元素: " << s.Top() << endl; // 输出3cout << "栈的大小: " << s.Size() << endl; // 输出3// 继续弹出栈中元素while (!s.Empty()) {cout << s.Top() << " ";s.Pop();}cout << endl;return 0;
}

C++实现的特点

1.数据与操作封装在一起:通过类的封装将数据和操作结合,使得操作更加安全和方便。

2.自动内存管理:利用构造函数和析构函数自动管理内存,无需手动调用初始化和销毁函数。

3.访问控制:可以使用private关键字将类的内部数据隐藏,防止外部直接访问,确保数据安全。

4.代码简洁:操作栈时不需要手动传递结构体指针,成员函数会自动使用this指针访问类的成员。

4.3 C++与C实现的对比总结

  1. 封装性:C++通过类的封装将数据和操作整合在一起,并且可以控制数据的访问权限(publicprivateprotected),从而提高了代码的安全性和可维护性。而在C语言中,所有数据成员都可以被外部随意修改,缺乏数据保护机制。

  2. 内存管理:C++使用构造函数和析构函数来管理资源,防止内存泄漏和资源浪费。而C语言需要手动管理内存,容易出现忘记释放资源的情况。

  3. 操作简便:C++使用面向对象的编程方式,使得操作对象更加直观。成员函数自动使用this指针,代码更加简洁。而在C语言中,操作数据时需要显式传递结构体指针。

  4. 代码扩展性:C++的类支持继承和多态,可以通过继承扩展类的功能,使得代码复用性和扩展性更强。而C语言没有这种机制,只能通过函数指针等手段来模拟多态。

五、总结

本文介绍了C++类与对象的基础知识,包括类的定义、访问限定符、类的作用域、实例化的概念、对象的大小、this指针的使用等内容。通过这些内容,我们初步了解了C++面向对象编程中的封装特性。C++中的类通过封装将数据和操作整合在一起,能够更好地保护数据的安全性并简化操作流程。同时,this指针的使用也为操作对象提供了便利。

尽管C++相较于C语言有诸多优点,但它的面向对象特性还包括继承和多态等内容,这些特性在构建复杂系统时显得尤为重要。后续的博客将深入探讨这些高级特性,帮助大家更好地掌握C++面向对象编程的精髓。希望这篇博客对你有所帮助,欢迎持续关注!

 

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

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

相关文章

基于SSM+微信小程序的房屋租赁管理系统(房屋2)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于SSM微信小程序的房屋租赁管理系统实现了有管理员、中介和用户。 1、管理员功能有&#xff0c;个人中心&#xff0c;用户管理&#xff0c;中介管理&#xff0c;房屋信息管理&#xff…

Java基础-IO基础

IO是指input/output&#xff0c;即输入和输出。输入和输出是以内存为中心的&#xff1a; input 从外部往内存输入数据&#xff0c;比如硬盘中的数据写入内存等。 output 从内存往外输出数据&#xff0c;比如内存数据写入硬盘等。 File File类表示一个文件或者一个目录。使用F…

【服务器虚拟化是什么?】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

父子元素中只有子元素设置margin-bottom的问题

问题代码如下所示 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style>.div1 {background-color: red;width: 80px;height: 80px;border: 1px solid orange;}.div2 {bac…

【飞腾加固服务器】全国产化解决方案:飞腾FT2000+/64核,赋能关键任务保驾护航

在信息安全和自主可控的时代背景下&#xff0c;国产化设备的需求与日俱增&#xff0c;尤其是在国防、航空航天、能源和其他关键行业。高可靠性和极端环境设计的国产加固服务器&#xff0c;搭载强大的飞腾FT2000/64核处理器&#xff0c;全面满足国产自主可控的严苛要求。 性能强…

光伏电站设计之辐照度效果(threejs实现)

类似 solaredge里面的日照度效果 1、由经纬度和屋顶朝向获取&#xff08;参考pvlib&#xff09;当前地区的辐照度值&#xff0c; 2、根据辐照度值插值获取对应辐照度的颜色。 3、计算片段着色器里面计算每个顶点的遮挡率和紫色混合 4、计算鼠标移动中的投射屋顶位置辐照度&…

Ansible自动化运维管理工具

一、Ansible 1.1、自动化运维管理工具有哪些&#xff1f; 工具架构语言使用情况Ansible无clientpython 协议用ssh95%puppetC/Sruby 协议用http基本不用chefC/Sruby 协议用http基本不用saltstackC/Spython 协议用ssh5% 1.2、Ansible简介 Ansible是一个基于Py…

网易翻译工具解析!这几大翻译器值得一试!

翻译工具的出现&#xff0c;使得跨语言沟通变得更加便捷。本文将为您推荐几款优秀的翻译工具&#xff0c;包括福昕在线翻译、福昕翻译客户端、海鲸AI翻译和网易有道翻译&#xff0c;帮助您在学习、工作和生活中轻松应对语言挑战。 福昕在线翻译 直达链接&#xff08;复制到浏…

c4d渲染和3d渲染有什么区别?c4d和3dmax哪个容易学?

在现代设计和创意产业中&#xff0c;3D渲染技术是不可或缺的一部分。它能够帮助设计师和艺术家将他们的创意转化为逼真的视觉效果&#xff0c;从而更好地展示和传达他们的想法。在众多3D渲染软件中&#xff0c;C4D渲染和3D Max是两款备受关注的软件。 本文将探讨C4D渲染和3D渲…

深度学习领域,你心目中 idea 最惊艳的论文是哪篇?

深度学习发展至今&#xff0c;共经历了三次浪潮&#xff0c;20 世纪40年代到60年代深度学习的雏形出现在控制论(cybernetics)中&#xff0c;20 世纪 80 年代 到 90 年代深度学习表现为 联结主义(connectionism)&#xff0c;直到 2006 年&#xff0c;才真正以深度学习之名复兴。…

Android中的内容提供者

目录 1.创建内容提供者 1--手动创建一个Android应用程序 2--创建自定义的内容提供者 2.访问其他应用程序 1. 解析URI 2. 查询数据 3. 遍历查询结果 3)案例:读取手机通信录 1.声明权限 2.activity_main.xml文件内容 3.my_phone_list.xml文件内容 4.定义PhoneInfo实体 5.定义MyPh…

现代大数据架构Kappa

现代大数据架构中的Kappa架构是一种处理大数据的架构&#xff0c;它作为Lambda架构的替代方案出现&#xff0c;旨在简化数据处理流程。以下是对Kappa架构的详细介绍&#xff1a; 一、核心思想 Kappa架构的核心思想是简化数据处理流程&#xff0c;通过使用单一的流处理层来同时…

就是这个样的粗爆,手搓一个计算器:热量计算器

作为程序员&#xff0c;没有合适的工具&#xff0c;就得手搓一个&#xff0c;PC端&#xff0c;移动端均可适用。废话不多说&#xff0c;直接上代码。 HTML: <div class"calculator"> <label for"weight">体重 (kg):</label> <inpu…

Git之代已修改文件的目录高亮设置

不管Android Studio或者Idea&#xff0c;进入Setting 选择如图所示&#xff0c;并进行勾选 就可以高亮了。

【C++】类的默认成员函数:深入剖析与应用(上)

&#x1f600;在上一篇文章中我们初步了解了C的基础概念&#xff0c;现在我们进行对C类的默认成员函数进行更加深入的理解&#xff01; &#x1f449;【C新手入门指南&#xff1a;从基础概念到实践之路】 目录 &#x1f4af;前言 &#x1f4af;构造函数 一、构造函数的定义…

Ambari-2.7.4和HDP-3.1.4安装(附Ambari和HDP安装包)

1.、环境及软件准备 Ambari-2.7.4和HDP-3.1.4下载 ,提取码:3rwq 环境:CentOS7(我这里使用的是CentOS7.9版本)、三台虚拟机,单节点内存13GB、存储80GB 软件:mysql5.7+、jdk8、ambari-2.7.4.0-centos7.tar.gz、HDP-3.1.4.0-centos7-rpm.tar.gz、HDP-UTILS-1.1.0.22-centos7…

Nodejs使用http模块创建Web服务器接收解析RFID读卡器刷卡数据

本示例使用设备&#xff1a; https://item.taobao.com/item.htm?spma21dvs.23580594.0.0.1d292c1buHvw58&ftt&id22173428704 Javascript源码 //引用http模块创建web服务器&#xff0c;监听指定的端口获取以GET、POST、JSON等方式上传的数据&#xff0c;并回应驱动读卡…

阿里云数据库导出 | 数据管理(兼容数据库备份)

文章目录 1、数据库导出2、操作步骤3、DMS - Data Management Service 1、数据库导出 2、操作步骤 3、DMS - Data Management Service

C#的自定义对话框和提示窗体 - 开源研究系列文章

上次的应用因为需要用到对话框和提示窗体&#xff0c;然后系统自带的MessageBox界面个人又看不上&#xff0c;所以就想自己编写一个自定义的窗体&#xff0c;于是有了本文&#xff0c;具体的已经应用到笔者其它的应用里了。 1、 项目目录&#xff1b; 2、 源码介绍&#xff1b;…

Bootstrapping、Bagging 和 Boosting

bagging方法如下&#xff1a; bagging和boosting比较