C++: 类和对象(上)

news2024/12/27 13:13:18

文章目录

  • 1. 面向对象和面向对象初步认识
  • 2. 类的引入
  • 3. 类的访问限定符
  • 4. 类的定义
    • 类的两种定义方式
    • 成员变量名规则的建议
  • 5. 类的作用域
  • 6. 类的实例化
  • 7. 类对象模型
    • 计算类对象的大小 类的实际存储方式
  • 8. this指针
    • this指针的引入
    • this指针的特性

1. 面向对象和面向对象初步认识

c语言是面向过程的, 关注的是过程, 分析出求解问题的步骤, 通过函数调用逐步解决问题.

c++是基于面向对象的, 关注的是对象, 将一件事情拆分成不同的对象, 靠对象之间的交互完成.

在这里插入图片描述

这是计算机体系结构中八个伟大思想中的一个: 使用抽象简化设计–降低低层细节以提供给高层一个更简单的模型

2. 类的引入

C语言结构体中只能定义变量, 在c++中, 结构体内不仅可以定义变量, 也可以定义函数

  1. 在c++中 struct 升级成了类, 不用 typedef 重命名.
  2. 可以直接用 struct 后面的类名创建对象.

例如:

typedef int STDataType;
struct Stack
{
    void Init(size_t capacity)
    {
        _array = (STDataType*)malloc(sizeof(STDataType) * capacity);
        if (nullptr == _array)
        {
            perror("malloc fail");
            return;
        }

        _capacity = capacity;
        _top = 0;
    }

    void Push(const STDataType& data)
    {
        _array[_top] = data;
        _top++;
    }

    void Destroy()
    {
        if (_array)
        {
            free(_array);
            _array = nullptr;
            _capacity = _top = 0;
        }
    }
    STDataType* _array;
    int _capacity;
    int _top;
};

int main()
{
    Stack s;
    s.Init(10);
    s.Push(1);
    s.Push(2);
    s.Push(3);
    s.Push(4);
    s.Push(5);

    s.Destroy();

    return 0;
} 
  1. 类名就是类型, 创建对象的时候不需要加 struct, 直接 Stack 即可
  2. 类里面可以定义成员函数, 调用函数可直接用 结构体变量 + . + 成员函数的格式

很明显, c++中的类更为精简.

在这里插入图片描述

在c++中更喜欢用class来代替struct进行类的定义

3. 类的访问限定符

c++实现封装的方式: 用类将对象的属性和方法结合在一块, 让对象更加完善, 通过访问权限选择性的将接口提供给外部的用户使用

通过权限访问符可以一定程度上保护类的成员. C++ 访问限定符一共有三种:

在这里插入图片描述

  1. public 修饰的成员在类外可以直接被访问.
  2. protectedprivate 修饰的成员在类外不能直接被访问.(此处protectedprivate 是相似的)
  3. 访问权限作用在从该访问限定符出现的位置一直到下一个访问限定符出现为止.
  4. 如果后面没有访问限定符, 作用域就到 } 结束
  5. class 的默认访问权限默认是 private; struct 的默认访问权限默认是 public (要兼容c语言)

注意: 访问限定符只在编译时有用, 当数据映射到内存后, 没有任何访问限定符上的区别

通常来说, 将类的成员变量的访问权限设置成 private.

  1. s.Empty()(成员变量设置成私有) 和 s.top == 0(成员变量设置成公有). 两者相比肯定是前一种更好. 前一种更为规范, 既然同一抽象层次的都是使用这一种格式, 例如 s.Init(10) s.Push(1), 显然前一种更好.
  2. s.Empty()见名知意, 知道这个函数返回的是栈对象是否空的结果. 而 s.top == 0 需要更加进一步思考, 代码可读性明显降低了.

问题: C++中 structclass 的区别是什么?
解答:

  • C++ 需要兼容 C语言, 所以 C++ 中可以使用 struct 定义结构体.
  • 另外 C++ 也可以用 struct 定义类. 和 class 定义类是一样的. 结构体名同时也是类名
  • 不同的是 struct 定义的类默认访问权限是 public; class 定义的类默认访问权限是 private

4. 类的定义

class ClassName
{
    // 类体: 由成员函数和成员变量组成

};  // 一定要注意后面的分号
  1. class 为定义类的关键字, ClassName 为类的名字, {} 中为类的主体, {} 内为一个域. 注意类定义结束后面分号不能省略.
  1. 类体中内容被称为类的成员: 类中的变量称为类的属性成员变量;类中的函数称为类的方法或者成员函数.

类的两种定义方式

  1. 声明和定义全部放在类体中, 需注意: 成员函数如果在类中定义, 编译器可能会将其当成内联函数处理.

在这里插入图片描述

  1. 类声明放在头文件 .h 文件中, 成员函数定义放在 .cpp 文件中, 注意: 定义成员函数名前需要加类名::(每一个类是一个域, 需要用到::来指明函数是在哪个域中间的)

在这里插入图片描述

更推荐使用第二种定义方法, 在类中定义的成员函数默认是 inline 的, 也可以直接将短小的成员函数直接放在类定义中.

正确用法: 长函数和声明分离, 短小函数声明定义不分离.


成员变量名规则的建议

如果我们定义了一个 Date 类, 需要写一个初始化年份的成员函数, 那么下面的代码中的两个 year 分别指什么呢?

这里两个 year 都是函数创建的局部变量, 会自我赋值, 与初衷相悖.

在这里插入图片描述

为了进行区别, 一般内部的成员变量命名都会在前面添加一个 _, 例如 _year

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 d;
    d.Init(2020, 1, 1);
    d.Print();

    return 0;
}

程序运行结果如下:
在这里插入图片描述

5. 类的作用域

类定义了一个新的作用域, 类的所有成员都在类的作用域中. 在类体外定义成员时, 需要使用 :: 作用域操作符指明成员使用哪个类域.

typedef int STDataType;

class Stack
{
public:
    void Init(int capacity); 
private:
    STDataType* _array;
    int _capacity;
    int _top;
};

// 这里需要指定 Init 是属于 Stack 这个类域的
void Stack::Init(int capacity)
{
  _array = (STDataType*)malloc(sizeof(STDataType) * capacity);
  if (nullptr == _array)
  {
    perror("malloc fail");
    return;
  }

  _capacity = capacity;
  _top = 0;
}

6. 类的实例化

用类类型创造对象的过程, 称为类的实例化.

  1. 类是对对象进行描述的, 是一个模型一样的东西, 定义出一个类并没有分配实际的内存空间来存储它.

就拿 int 来说, 类和 int 在某些方面是一样的, 指明接下来创建的对象是什么类型的. 我直接对 int 进行赋值显然是不可能的, 类也一样.

变量的声明和定义的区别就是是否开了内存空间, int 告诉编译器如果我需要定义一个 int 类型的变量, 需要让操作系统给我分配一块 4字节 大小的空间. 只有当真正我定义了一个变量(int a;), 这个时候才会真正有一个 int 类型的对象存在.

依次类推, 类的声明也让编译器告诉操作系统, 如果我需要创建一个类类型的对象, 操作系统要给我分配这么大的空间, 相当于一个告示, 一个提醒. 只有当用类真正创建了一个具体的对象的时候, 内存中才会有一个具体的类类型的对象存在.

  1. 一个类可以实例化出多个对象, 实例化出的对象 占用实际的物理空间, 存储类成员变量
int main()
{
    Date._year = 1000;      // 编译失败, 语法错误

    return 0;
}

Date 类是没有具体空间的, 只有 Date 类实例化出的对象才有具体的年月日.

  1. 打个比方. 类实例化出对象就像现实中使用建筑设计图建造出房子, 类就像是设计图, 只是蓝图, 但是并没有实际的建筑存在, 同时类也只是一个设计, 实例化出的对象才能实际存储数据, 占用物理空间.

在这里插入图片描述

需要注意的是, 成员函数(类的方法)不存在在对象里, 它实际就是正常的函数, 只是限定在类这个域中, 调用成员函数和调用正常函数的操作是一样的. c++的命名规则会将成员函数和正常函数做出区分.

就比如一个小区, 每家每户的图纸都是一样的, 具体每一户内部的物品是不一样的.对应于类创建的多个实例化对象的成员变量可以不一样的.

每个小区都有公共的停车场, 花园等等, 但这些不需要特指是哪一家的, 只需要知道是这个小区的就行了. 对应于类的成员函数不需要用类类型生成的对象存储, 只要该对象是这个类类型的, 就可以调用该类的成员函数.

7. 类对象模型

计算类对象的大小 类的实际存储方式

为了验证 C++ 中类的对象只存放了成员变量, 写下了如下代码:

typedef int STDataType;
class Stack
{
private:
    STDataType* _array;
    int _capacity;
    int _top;
public:
    void Init(int capacity);
    void Push(STDataType x);
};

class A
{
    
};

class B
{
private:
    char _a;
public:
    void printB()
    {
        cout << _a << endl;
    }
};

class C
{
private:
    char _a;
};

int main()
{
    Stack st;   //类实例化一个对象
    cout << sizeof(st) << endl;
    cout << sizeof(Stack) << endl;
    cout << sizeof(A) << endl;
    cout << sizeof(B) << endl;
    cout << sizeof(C) << endl;

    return 0;
}

程序运行如下:

在这里插入图片描述
在这里插入图片描述

一个类的大小, 实际上就是该类中"成员变量"之和, 当然要注意内存对齐
注意空类的大小, 空类比较特殊, 编译器给了空类一个字节来唯一表示这个类的对象


由此可知: 类的对象存储方式是这样的
将成员变量存放到类实例化的对象内, 类的成员函数则放置在一个类成员函数表, 在公共代码区.

在这里插入图片描述

结构体对齐规则详见

8. this指针

this指针的引入

先定义一个日期类 Date

class Date
{
public:
    void Init(int year, int month, int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    void Print(Date* const this)
    {
        cout << this->_year << '-' << this->_month << '-' << this->_day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d1, d2;
    d1.Init(2000, 1, 1);
    d2.Init(2010, 10, 10);
    d1.Print();
    d2.Print();

    return 0;
}

程序正常运行:
在这里插入图片描述

但是还是有一个问题:
Date 类中有 InitPrint 两个成员函数, 函数体中并没有关于对象的区分, 那么当 d1 调用成员函数 Init 时, 该函数是如何知道应该设置 d1 对象, 而不是设置 d2 对象呢?

C++ 通过引入 this 指针解决该问题

C++ 编译器给每个"非静态的成员函数"增加了一个隐藏的指针函数, 让该指针指向当前对象(函数运行时调用该函数的对象), 在函数体中所有"成员变量"的操作, 都是通过该指针去访问.

只不过所有的操作对用户是透明的, 即用户不需要来传递, 编译器自动完成.

在这里插入图片描述

this指针的特性

  1. this指针的类型: 类类型* const, 即成员函数中, 不能给 this 指针赋值
  1. 只能在成员函数的内部使用
  1. this指针本质上是成员函数的形参, 当对象调用成员函数时, 将对象地址作为实参传递给 this 形参. 所以对象中不存储 this 指针
  1. this 指针是成员函数第一个隐含的指针形参, 一般情况由编译器通过寄存器自动传递, 不需要用户传递.

this 指针存放在哪里?

this 指针是形参, 一般存放在栈空间中.

this 指针可以为空吗?

  • 下面程序编译运行结果是? A. 编译报错 B. 运行崩溃 C. 正常运行

class A
{
public:
	void Print()
	{
		cout <<"Print()" << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

程序正常运行.
在这里插入图片描述

成员函数放置在公共代码区内, 调用成员函数不需要对对象进行解引用, 同时成员函数内部也没有访问到成员变量, 不会发生问题.


  • 下面程序编译运行结果是? A. 编译报错 B. 运行崩溃 C. 正常运行
class A
{
public:
	void Print()
	{
		cout << _a << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

程序运行出错:
在这里插入图片描述

程序运行期间发生了空指针解引用, 成员函数访问到对象的成员变量了, 然而 this 指针是一个空指针.

空指针解引用是运行崩溃.

本章完.

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

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

相关文章

c++指针【1】

在C中&#xff0c;指针是一种特殊的变量&#xff0c;它存储了一个内存地址。C指针在处理内存、数组、函数参数传递、文件I/O、动态内存分配等方面有着重要的应用。 一个指针变量通常被声明为特定类型的指针。例如&#xff0c;一个整数类型的指针可以指向一个整数。在声明指针变…

【软考】13. 结构化开发方法

《系统分析与设计概述》 当前系统的物理模型 ——> 当前系统的逻辑模型 ——> 目标系统的逻辑模型 ——> 目标系统的物理模型系统开发的目的&#xff1a;当前系统的物理模型 ——> 目标系统的物理模型 系统设计基本原理 抽象、模块化&#xff08;逐步分解&#xf…

「网络编程」数据链路层协议_ 以太网协议学习

「前言」文章内容是数据链路层以太网协议的讲解。 「归属专栏」网络编程 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 一、以太网协议简介二、以太网帧格式&#xff08;报头&#xff09;三、MTU对上层协议的影响四、ARP协议4.1 ARP协议的作用4.2 ARP协议报头 一、以太网协…

【Spring】IOC快速入门

文章目录 1. Spring简介2. IOC快速入门 1. Spring简介 Spring是一个开放源代码的Java SE/EE一站式轻量级开源框架&#xff0c;由Rod Johnson发起并创立。其核心是IOC&#xff08;控制反转&#xff09;和AOP&#xff08;面向切面编程&#xff09;&#xff0c;使得开发者可以将对…

『 C++类与对象』继承

文章目录 继承的概念继承方式与访问限定符基类和派生类对象赋值转换继承中的作用域隐藏 派生类的默认成员函数构造函数拷贝构造函数赋值运算符重载析构函数 继承与友元静态成员与继承关系 继承的概念 继承的机制为,允许在以该类为基础上对类进行扩展,增加功能; 通常原来也就是…

社区买菜系统 JAVA开源项目

目录 项目内容 项目获取 项目截图 项目内容 基于VueSpringBootMySQL的社区买菜系统&#xff0c;包含菜品分类模块、菜品档案模块、菜品订单模块、菜品收藏模块、收货地址模块&#xff0c;还包含系统自带的用户管理、部门管理、角色管理、菜单管理、日志管理、数据字典管理、…

【网安AIGC专题10.19】论文6:Java漏洞自动修复+数据集 VJBench+大语言模型、APR技术+代码转换方法+LLM和DL-APR模型的挑战与机会

How Effective Are Neural Networks for Fixing Security Vulnerabilities 写在最前面摘要贡献发现 介绍背景&#xff1a;漏洞修复需求和Java漏洞修复方向动机方法贡献 数据集先前的数据集和Java漏洞Benchmark数据集扩展要求数据处理工作最终数据集 VJBenchVJBench 与 Vul4J 的…

Unity编辑器扩展之CustomPropertyDrawer理解

一、引言&#xff0c; 在上一篇文章中提到&#xff0c;CustomEditor只能自定义单一类&#xff0c;被其他类持有的类自定义没有作用&#xff0c;这个时候就需要使用CustomPropertyDrawer属性。 二、PropertyDrawer介绍 PropertyDrawer用于自定义属性绘制器的基类。使用Proper…

【办公自动化】wps word首字下沉/文字宽度/段落底纹/图片缩放/装订线(Word的相关操作)

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

『第九章』雨燕新量子引擎:结构化并发

在本篇博文中,您将学到如下内容: 1. 千呼万唤始出来:结构化并发2. async/await3. “结构化(structured)”到底是个神马?3.1 async let3.2 TaskGroup4. 非结构化并发(unstructured concurrency)4.1 非异步上下文中的 Task4.2 Detached Task4.3 延时5. 任务(Task)的取消和…

168. Excel表列名称

168. Excel表列名称 Java代码&#xff1a; 26进制&#xff0c;但是每个进制是从1开始的&#xff0c;不是从0开始&#xff1b;因此要计算要构建从0开始的求余&#xff01; class Solution {public String convertToTitle(int cn) {StringBuilder sb new StringBuilder();whi…

ElasticSearch快速入门实战

全文检索 什么是全文检索 全文检索是一种通过对文本内容进行全面索引和搜索的技术。它可以快速地在大量文本数据中查找包含特定关键词或短语的文档&#xff0c;并返回相关的搜索结果。全文检索广泛应用于各种信息管理系统和应用中&#xff0c;如搜索引擎、文档管理系统、电子…

Xtuner——报错解决汇总

文章目录 load_dataset读取jsonl文件报错 load_dataset读取jsonl文件报错 alpaca_en dict(typeprocess_hf_dataset,datasetdict(typeload_dataset, data_filesalpaca_file_path),tokenizertokenizer,max_lengthmax_length,dataset_map_fnalpaca_map_fn,template_map_fndict(t…

【C++初阶(三)】引用内联函数auto关键字

目录 前言 1. 引用 1.1 引用的概念 1.2 引用的特性 1.3 引用的权限 1.4 引用的使用 1.5 引用与指针的区别 2. 内联函数 2.1 什么是内联函数 2.2 内联函数的特性 3. auto关键字 3.1 auto简介 3.2 auto使用规则 3.3 auto不能使用的场景 4. 基于范围的for循环 4.1 范围for…

一文2000字教你从0到1实现Jmeter 分布式压测

你可以使用 JMeter 来模拟高并发秒杀场景下的压力测试。这里有一个例子&#xff0c;它模拟了同时有 5000 个用户&#xff0c;循环 10 次的情况‍。 请求默认配置 token 配置 秒杀接口 ​结果分析 ​但是&#xff0c;实际企业中&#xff0c;这种压测方式根本不满足实际需求。下面…

技术资料MF74:将图像插入单元格注释

【分享成果&#xff0c;随喜正能量】须知往生净土&#xff0c;全仗信、愿。有信、愿&#xff0c;即未得三昧、未得一心不乱&#xff0c;亦可往生。且莫只以一心不乱&#xff0c;及得念佛三昧为志事&#xff0c;不复以信、愿、净念为事。。 我给VBA的定义&#xff1a;VBA是个人…

讯飞星火大模型V3.0 WebApi使用

讯飞星火大模型V3.0 WebApi使用 文档说明&#xff1a;星火认知大模型Web文档 | 讯飞开放平台文档中心 (xfyun.cn) 实现效果 初始化 首先构建一个基础脚手架项目 npm init vuelatest用到如下依赖 "dependencies": {"crypto-js": "^4.2.0",&q…

ClickHouse快速了解

简介 ClickHouse是一个开源列式数据库管理系统&#xff08;DBMS&#xff09;&#xff0c;用于在线分析处理&#xff08;OLAP&#xff09;&#xff1a; 列式存储&#xff1a;与传统的行式数据库不同&#xff0c;ClickHouse以列的形式存储数据&#xff0c;这使得在分析大量数据时…

嵌入式中C++ 编程习惯与编程要点分析

以良好的方式编写C class 假设现在我们要实现一个复数类complex&#xff0c;在类的实现过程中探索良好的编程习惯。 ① Header(头文件)中的防卫式声明 complex.h: # ifndef __COMPLEX__ # define __COMPLEX__ class complex {} # endif 防止头文件的内容被多次包含。 …

2.25每日一题(反常积分的计算:被积函数分母出现e的正负x次幂)

注&#xff1a;被积函数分母出现e的正负x次幂&#xff0c;这种情况需要把分母化成全部都是正次幂的情况再进行计算