C++_15_类与对象

news2024/11/13 10:18:33

类与对象

什么是类?

描述有共同特征的事务的概念

作用:代码中 创建对象

什么是对象?

生活中: 就是指真实存在的事物。

代码中: 模拟真实的事物,使用类创建得到。

类与对象的关系

生活中:

​ 先有 对象 后有 类

代码中:

​ 现有 类 后有 对象

一个类 可以创建多个对象

多个对象可以属于 同一个类

多个对象之间 互不干扰

如何定义一个类

class 类名
{
成员变量:【定义在类中的变量】
	描述事物的静态特征   如人的 身高、肤色、身高、   一个类可以有无数个成员变量  也可以没有
构造函数:创建对象
析构函数:销毁回收对象
拷贝构造:对象赋值给对象 
成员函数:
	描述事物的动态特征  如吃饭、睡觉、喝水、走路....
}

示例:

定义一个人类

成员变量: 姓名,年龄,身高

成员函数: 吃饭 睡觉

#include <iostream>
using namespace std;
class Person
{
   char name[50]; // 避免使用指针 发生浅拷贝 和结构体一样
   int age;
   int height;

   void eat(char *foodName)
   {
          cout << name << "吃" << foodName << endl;
   }
   void sleep(); // 声明和定义可以分开
};
void Person::sleep()
{
   cout << name << "睡觉" << endl;
}
int main(int argc, char const *argv[])
{
   return 0;
}

注意:

因为类的定义只需定义一次,所以我们一般将其写在头文件中
但是类中有成员函数,头文件中对函数只声明不实现
所以在头文件中定义类,

类定义以及创建对象 的标准格式:

定义和声明分开 反正后面都这样写

头文件

#ifndef xxz // 判断是否存在宏
#define xxz // 定义宏
using namespace std;
#include <iostream>
class Person
{
public:            // 修饰符
   char name[50]; // 避免使用指针char *name 发生浅拷贝 和结构体一样
   int age;
   int height;
   void eat(char *foodName);
   void sleep(); // 声明和定义可以分开
};
#endif

源文件

#include "01.h"
//  :: 作用域修饰符
void Person::eat(char *foodName)
{
cout << name << "吃" << foodName << endl;
}
void Person::sleep()
{
cout << name << "睡觉" << endl;
}

使用文件

#include <iostream>
#include "01.h"
#include <string.h>
int main(int argc, char const *argv[])
{
   // 创建对象
   Person p1;
   p1.age = 18;
   p1.height = 180;
   strcpy(p1.name, "小农民");
   cout << "p1.name " << p1.name << endl;
   p1.eat("米饭");
   return 0;
}

访问权限修饰符

类中成员 不加修饰符 默认使用 私有的 private 修饰

但是 后面创建类的时候

推荐

成员变量 使用 private 修饰

成员函数 使用 public 修饰

直接操作值 就比如 你定义 age 别人直接使用 给赋值 负数 那就会出错

所以 将成员变量全写私有 函数写公共 通过函数 get set 成员变量 来限定各个成员变量 。

作用: 限定类中成员的访问范围

在这里插入图片描述

public:  程序任何一处 都可直接使用

protected: 当前类中或子类中使用

private:	当前类中使用

使用:

class 类名
{
    private:
    	成员
    protected:
    	成员
    public:
     	成员
}

类的设计(封装性)

  • 1,私有化其成员变量
  • 2,公共其成员函数
  • 3,提供对成员变量操作的get与set函数
class Person
{
private:
    int age = 0;
    int height = 0;
    char *name = NULL;

public:
    void eat()
    {
        cout << name << "吃饭" << endl;
    }
    void sleep();
    void setAge(int a)
    {
        // this:那个对象调用该函数this就是该对象的指针变量
        // this->age = age;
        if (a < 0)
        {
            this关键字
                概念
                    作用
                        age = 0;
            return;
        }
        age = a;
    }
    int getAge()
    {
        return age;
    }
    void setHeight(int h)
    {
        height = h;
    }
    int getHeight()
    {
        return height;
    }
    void setName(char *n)
    {
        if (name == NULL)
        {
            return;
        }
        strcpy(name, n);
    }
};
// 虽然函数的声明与实现分离
// 实现在类外,但是依据可以直接使用priate修饰的成员
void Person::sleep()
{
    cout << name << "睡觉" << endl;
}

this关键字

不写它也有 是忽略显示了 而不是没有

概念:

谁调用this所在的函数,this就代表谁,是个指针

作用:

1,在当前类中调用当前类的成员,此时this可以忽略不写
2,当局部变量与成员变量重名
		变量名 局部变量
		this->变量名 成员变量

在这里插入图片描述

1 构造函数

注意:

  • 类中没有自定义 构造函数 系统将提供一个无参构造
  • 要有自定义构造函数,系统就不会为其提供无参构造
  • 一个类中可以有多个构造函数,这多个构造函数的关系是重载

作用: 创建本类对象时调用

语法:

类名 (形参列表)
{
	函数体;
}

调用:

调用无参构造:
			类名  对象名;
调用有参构造:
			类名   对象名(实参列表)

示例:

#include <iostream>
#include <string.h>
using namespace std;
class Person
{
private:
    int age;
    char name[50];

public:
    // 无参构造
    Person()
    {
        cout << "无参构造被调用" << endl;
    }
    //  有构造函数  没有 返回值类型 所以一定没有返回值
    Person(int age, char *name)
    {
        this->age = age;
        strcpy(this->name, name); // 用 深拷贝
        cout << "有参被调用" << endl;
    }
};
int main(int argc, char const *argv[])
{
    // 此时并没有书写构造函数 但还是可以创建类对象
    Person p1; // 默认调用的就是无参构造
    cout << "==============" << endl;
    Person p2(18, "张十一");
    return 0;
}

2 析构函数

析构函数 没有参数

且一个 类 只能写 一个析构函数

如果不写系统默认提供一个默认的析构函数

作用: 当对象销毁时调用

语法:

~类名()
{
	函数体;
}

示例:

#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
// 析构函数
class Person
{
private:
    char *name;

public:
    Person()
    {
        cout << "person 的无参构造" << endl;
        name = (char *)calloc(50, sizeof(char));
    }
    Person(char *name)
    {
        cout << "person 的有参构造" << endl;
        this->name = (char *)calloc(50, sizeof(char));
        strcpy(this->name, name);
    }
    // 析构函数没有参数   销毁释放内存
    ~Person()
    {
        cout << name << "\t" << "销毁了~~~" << endl;
        free(name);
    }
};

int main(int argc, char const *argv[])
{
    // 此时属于局部变量
    //  生命周期:
    // 随着 函数调用而生成 随着函数的执行结束而销毁
    Person p1;
    Person p("张三");
    return 0;
}

3 拷贝构造

而系统提供的就是 浅拷贝的方式 所以 一般都自己写

浅拷贝:

假设 指针A 指向了 堆区的 内存 再后面 有指针B 将指针A 的值赋值给指针B 所以 指针A 和 指针B 将指向同一块内存

在这里插入图片描述

假设指针A和B分别是对象A和B的两个成员,这样会产生两个问题:

在这里插入图片描述

问题一:

不管是 对象A 还是 对象B 只要其中一个对象 修改了内存中的数据 另一个对象 看到的数据 也会改变。

问题二:

一般会将释放内存的代码存放在析构函数中,但要是如果对象A 和 对象B其中有一个销毁了,调用了析构函数

其中一个对象释放了内存,另一个对象的指针就成了野指针。

深拷贝:

概念:

在这里插入图片描述

如果有指针A 指向一块内存,那么就重新分配一块相同大小的内存,让指针B指向新内存。

然后再把指针A 指向内存中的数据 拷贝到新内存中,

这样的拷贝方式很彻底,拷贝之后,大家各自操作自己的指针和内存。就不会有任何冲突

深拷贝步骤:

  • 1 分配内存

  • 2 拷贝数据

是为了防止 浅拷贝

就是传引用 &p 别传本身 p 会多占内存

作用:

这个类的对象A 赋值给这个类的对象B 时会触发拷贝构造
		类名  对象名A;
		类名  对象名B = 对象名A;

语法:

类名(const 类型 &别名)
{
 	函数体;
}

注意:

此时不会触发构造函数
注意浅拷贝,
此时一定要进行深拷贝,避免重复释放导致的崩溃    !!!!	

初始化列表

语法:

# 类名(形成列表):成员变量名(形参中的变量名),成员变量名(形参中的变量名),.....
{
 	函数体;
}
class A
{
private:
    int x;
    int y;
    int z;

public:
    A()
    {
    }
    A(int x, int y, int z) : x(x), y(y), z(z)
    {
    }
} A a1(1, 11, 111);
A a2(2, 22, 222);

注意:

基本类型除外实现的是浅拷贝

隐式转换

当调用的构造函数 只有一个参数的时候可以进行隐式转换 多参就不能使用

class A
{
private:
    int x;
    int y;
    int z;

public:
    A()
    {
    }
    A(int x) : x(x)
    {
    }
} 
A a1 = 10;
A a2 = 20;

语法:

#  类名 对象名 = 实参;

explicit 关键字

作用: 禁止隐式 转换

语法:

explicit 类名(形参) { }

new与delete

new : 在堆内存中 创建对象

delete : 释放对象 堆内存

new

语法:

类名 *对象指针 = new 类名;  // 调无参
 
类名 *对象指针 = new 类名(实参列表);  //调有参   

步骤:

> 先在堆中开辟内存
> 再调用构造函数

delete

作用:释放对象 堆内存

语法:

delete 对象指针;

步骤:

>先 执行 析构函数
> 再释放对象指针指向内存

	和  new 相反

类 B的对象作为类 A的成员变量

创建时构造函数的调用顺序

先成员构造
在自己构造

销毁时析构函数的调用顺序

先自己析构
在成员析构

注意:

使用new创建A类对象时,依然会先创建其成员类B的对象
		先成员构造
		后自己构造
使用new是在堆中开辟的,无法自动释放,需要使用delete,此时
		先自己析构
		后成员析构
class B{
public:
	char *name = (char *)calloc(50,1);
~B()
{
	free(name);
}
}
class A{
	B b;
}
int main()
{
	A *a = new A;
	delete a;
}

对象数组

作用:

​ 存储对象的数组

静态创建

动态创建:

【 是在堆区创建的 】

 类名  *数组名 = new 类名[]{对象1,对象2,对象3,...};

注意:
	释放语法:
			delete 数组名;  // 只释放了一个
			delete [] 数组名;   //释放所有

静态成员

概念:

​ 使用 static 修饰的 成员为 静态成员

修饰成员变量的特点

1,使用static修饰的成员变量属于该类,给类的所有对象共同持有一份
2,不占用对象的内存空间,该成员变量存储在静态全局区
3,可以使用类名直接调用
	类名::成员变量名
	类名::成员变量名 =;
注意:
	修饰的成员变量需要类外初始化,其语法为

	数据类型 类名::变量名 =;

	如果不初始化会报该成员变量未定义

修饰成员函数的特点:

1, 可以通过类名直接调用,也可以使用该类对象调用
注意:
2. static修饰的成员函数中只能使用该类的静态成员,static修饰的成员函数中不能使用this关键字

为什么不能使用this关键字

因为静态成员函数可以使用类名直接调用,此时this没有可代表的对象

const修饰的成员函数

语法:

返回值类型  函数名(形参列表) const
{
    
}
// 特点: 使用这个函数的时候 只能读值  不能修改成员值

示例:

#include <iostream>
using namespace std; 
class Dog
{
private:
    int age;
public:
    //有参构造
    Dog(int age)
    {
        this->age = age;
        cout<<"有参构造调用!"<<endl;
    }
    //拷贝构造
    Dog(const Dog &dog)
    {
        cout<<"拷贝构造调用!"<<endl;
        this->age = age;  
    }
    // void show()const;
    void show();  
};
// void Dog::show()const 
void Dog::show()
{
    age=101;   // 这样就可以改值 
    cout<<"狗狗 "<< age<<"岁"<<endl;
}
int main(int argc, char const *argv[])
{
    Dog d1(12);  //类对象创建
    d1.show();  //对象调用函数
    return 0;
}

链式编程

#include <iostream>
using namespace std;
class A
{
public:
A *test01()
{
  cout << "test 01" << endl;
  return this;
}
A *test02()
{
  cout << "test 02" << endl;
  return this;
}
A *test03()
{
  cout << "test 03" << endl;
  return this;
}
A *test04()
{
  cout << "test 04" << endl;
  return this;
}
};
int main(int argc, char const *argv[])
{
A a;
// a.test01();
// a.test02();
// a.test03();
// a.test04();
a.test01()->test02()->test03()->test04()->test01();
return 0;
}

在这里插入图片描述

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

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

相关文章

VMware vSphere5.0关闭虚拟机电源时,报错从ESXI主机接收到错误

ESXI和VCENTER都是5.0版本的&#xff0c;有台虚拟机关机报错提示从ESXI主机接受到意外错误 具体报错信息如下&#xff1a; 从VCENTER平台对该虚拟机做任何操作都无法生效&#xff0c;后来查看了虚拟机的网络和端口&#xff0c;发现SSH能正常联通&#xff0c;进入虚拟机后使用命…

【算法专场】模拟(下)

目录 前言 38. 外观数列 算法分析 算法思路 算法代码 1419. 数青蛙 算法分析 算法思路 算法代码 2671. 频率跟踪器 算法分析 算法思路 算法代码 前言 在前面我们已经讲解了什么是模拟算法&#xff0c;这篇主要是讲解在leetcode上遇到的一些模拟题目~ 38. 外观数列…

Pencils Protocol生态新进展,即将上线 Vault 产品

“极高的盈利预期、通证的持续回购与销毁&#xff0c;Vault产品的推出正在成为Pencils Protocol生态发展的重磅利好。” Pencils Protocol是目前Scroll生态TVL最高的DeFi平台 &#xff0c;即便是行情整体较为平淡&#xff0c;其仍旧能够保持在3亿美元左右的锁仓价值&#xff0c…

史上最全的Linux常用命令汇总(超全面!超详细!)收藏这一篇就够了!

command &#xff1a;命令名&#xff0c;相应功能的英文单词或单词的缩写[-options] &#xff1a;选项&#xff0c;可用来对命令进行控制&#xff0c;也可以省略parameter &#xff1a;传给命令的参数&#xff0c;可以是 零个、一个 或者 多个 查阅命令帮助信息 -help 说明&…

LC1860C 后来怎么样了

这块芯片前身是大唐旗下联芯的LC1860C&#xff1b;这块传奇芯片在4G时代大放异彩&#xff0c;但是某些原因之后&#xff0c;技术打包转让给三家&#xff0c;分别是&#xff1a;小米&#xff0c;大疆&#xff0c;哲酷&#xff08;VIVO&#xff09;&#xff1b; 1、哲酷 哲酷目…

Infiniband网络架构的技术与性能分析

Infiniband格局寡头&#xff0c;性能占优 这篇文章探讨了网络交换机的性能优势&#xff0c;以及如何通过扩大模型参数量来提高语言模型的生成和预测能力。然而&#xff0c;计算约束对这种正向关系产生了重要影响&#xff0c;导致在相同的计算约束下&#xff0c;总存在最佳的模型…

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h> #include <stdlib.h> void shellSort(int data[], int n){// 划分的数组&#xff0c;例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量&#xff0c;换值int temp;…

基于java网页的纸业管理系统设计与实现

博主介绍&#xff1a;专注于Java .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用 感兴趣的可以…

【iOS】MVC设计模式

MVC 前言 如何设计一个程序的结构&#xff0c;这是一门专门的学问&#xff0c;叫做"架构模式"&#xff08;architectural pattern&#xff09;&#xff0c;属于编程的方法论。MVC 模式就是架构模式的一种。 它是Apple 官方推荐的 App 开发架构&#xff0c;也是一般…

C++中深拷贝与浅拷贝

描述&#xff1a; 在未定义显示拷贝构造函数的情况下&#xff0c;系统调用默认的拷贝函数——即浅拷贝&#xff0c;它能够完成成员的简单赋值拷贝操作。当数据成员中没有指针时&#xff0c;浅拷贝是可行的&#xff1b; 但当数据成员中有指针时&#xff0c;如果采用简单的浅拷…

C++ vectorOJ练习题

目录 136. 只出现一次的数字 118. 杨辉三角 26. 删除有序数组中的重复项 137. 只出现一次的数字ll 260. 只出现一次的数字 III 17. 电话号码的字母组合 JZ39 数组中出现次数超过一半的数字 136. 只出现一次的数字 采用异或运算的思路 异或运算的特性是&#xff0c;相同的…

多机编队—(1)ubuntu 配置Fast_Planner

文章目录 前言一、Could not find package ...二、使用error: no match for ‘operator’...总结 前言 最近想要做有轨迹引导的多机器人编队&#xff0c;打算采用分布式的编队架构&#xff0c;实时的给每个机器人规划出目标位置&#xff0c;然后通过Fast_Planner生成避障路径&…

【与C++的邂逅】--- string容器使用

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 与C的邂逅 本篇博客我们将来了解string容器本身以及接口的使用。 string是串&#xff0c;本质是一个字符数组&#xff0c;可以对其进行增删查改。 &am…

Camtasia2024破解版本电脑屏幕录像编辑神器全新体验

&#x1f31f; 屏幕录像与编辑神器——Camtasia2024全新体验 大家好&#xff01;今天我要来和大家安利一款让我彻底摆脱视频制作烦恼的神器——Camtasia2024&#xff01;&#x1f389; &#x1f308; 功能升级&#xff1a;更智能&#xff0c;更便捷 得提的是Camtasia 2024在功…

python的常用模块,必能覆盖你的需求

1.Request 把python的提示信息做到精细且覆盖广泛 2.Numpy 非常重要的库&#xff0c;最初学Python&#xff0c;第一个使用的就是这个。为Python提供了很多高级的数学方式 3.SciPy 是Python的算法和数学工具车&#xff0c;把很多科学家从RUby吸引到了python 4. P…

【车载开发系列】ParaSoft安装步骤介绍

【车载开发系列】ParaSoft安装步骤介绍 【车载开发系列】ParaSoft安装步骤介绍 【车载开发系列】ParaSoft安装步骤介绍一. 前言二. 安装步骤1. 双击安装包2. 选择安装语言3. 选择许可协议4. 选择软件安装位置5. 选择开始菜单文件夹6. 选择安装时的附加任务7. 安装准备完毕8. 执…

【小沐学OpenGL】Ubuntu环境下glfw的安装和使用

文章目录 1、简介1.1 OpenGL简介1.2 glfw简介 2、安装glfw2.1 直接命令二进制安装2.2 源码安装 3、测试glfw3.1 测试1&#xff0c;glfwglew3.2 测试2&#xff0c;glfwglad3.3 测试3 结语 1、简介 1.1 OpenGL简介 OpenGL作为图形界的工业标准&#xff0c;其仅仅定义了一组2D和…

PhotoZoom9怎么样?图片模糊怎么办?

DeepZoomPix的前身。PhotoZoom是一款新颖的、技术上具有革命性的对数码图片进行放大的工具。通常的工具对数码图片进行放大时&#xff0c;总会降低图片的品质&#xff0c;而这款软件使用了S-SPLINE Max技术 一种申请过专利的&#xff0c;拥有自动调节、高级的插值算法的技术&am…

PCIe总线-Linux内核PCIe设备枚举流程分析(十三)

1.简介 当系统启动时或者有新的PCIe设备接入时&#xff0c;PCIe主机会扫描PCIe总线上的PCIe设备&#xff0c;读取设备配置空间信息&#xff0c;建立设备的拓扑关系&#xff0c;然后为设备分配资源&#xff08;如内存空间、I/O空间、中断、总线编号等&#xff09;&#xff0c;最…

网络安全AI大模型训练从入门到精通

前言 2022年下半年&#xff0c;国内安全圈内开始完chatGPT&#xff0c;当时在安全圈内小火了一把。大家纷纷注册去体验一把&#xff0c;希望chatGPT能帮助解决日常安服渗透问题。当时以为仅此而已&#xff0c;谁知年后大火&#xff0c;随后以chatGPT为代表的大语言模型&#x…