C++编程(四) —— OOP

文章目录

  • 前言
  • 一、this指针
  • 二、构造和析构
  • 三、深拷贝浅拷贝
    • 浅拷贝
    • 深拷贝
  • 编程实践


前言

什么是OOP思想?

OOP语言的四大特征:
抽象,封装/隐藏,继承,多态

一、this指针

this指针=》类=》很多对象
一套成员方法是如何处理多个对象的?具体而言:

成员方法show() => 怎么知道处理哪个对象的信息? 答:具体对象的this指针。
成员方法init (name,price,amount) => 怎么知道把信息初始化给哪一个对象的? 答:具体对象的this指针。
类的成员方法一经编译,所有的方法参数,都会加一个this指针,接收调用该方法的对象的地址。

当有一实例化对象good1,good1调用成员方法show时,编译器会自动加入参数:
show(&good1);
当有一实例化对象good2,good2调用成员方法init时,编译器会自动加入参数:
init(&good2,name,price,amount);

二、构造和析构

OOP实现顺序栈。

要想实现一个类,共有一般放成员方法,私有一般放成员变量。
因为顺序栈要实现内存的扩充,所以需要使用数组指针。

class SeqStack
{
public:
	// 构造函数 
    SeqStack(int size = 10)
    {
        cout << " SeqStack() Ptr" << this << endl;
        _pstack = new int[size];
        _top = -1;
        _size = size;
    }

    // 自定义的拷贝构造函数 《= 对象的浅拷贝现在有问题了
    SeqStack(const SeqStack &src)
    {
        cout << "SeqStack(const SeqStack &src)" << endl;
        _pstack = new int[src._size];
        for (int i = 0; i <= src._top; ++i)
        {
            _pstack[i] = src._pstack[i];
        }
        _top = src._top;
        _size = src._size;
    }

    // 析构函数
    ~SeqStack() 
    {
        cout << this << " ~SeqStack()" << endl;
        delete[]_pstack;
        _pstack = nullptr;
    }
	
	void push(int val)
    {
        if (full()) resize();  // resize不想要用户调他
        _top++;
        _pstack[top] = val;
    }
    void pop()
    {
        if (empty()) return;
        --_top;
    }
    int top() { return _pstack[_top]; }
    bool empty() { return _top == -1; }
    bool full() { return _top == _size - 1; }


private:
	int *_pstack;   // 动态开辟数组,存储顺序栈的元素
	int _top;		// 指向栈顶元素的位置
	int _size;		// 数组扩容的总大小
	void resize()
    {
        int *ptmp = new int[_size * 2];
        for (int i = 0; i < _size; ++i)
        {
            ptmp[i] = _pstack[i];
        } // memcpy(ptmp, _pstack, sizeof(int)*_size); realloc
        delete[]_pstack;
        _pstack = ptmp;
        _size *= 2;
    }
}

析构函数如果需要释放一个指向数组的指针,需要中括号[]

delete[]_pstack;

调用:

SeqStack ps1;  // .data段开辟内存,程序结束才进行析构
int main() 
{
	SeqStack *ps2 = new SeqStack(60);   //heap段开辟内存
	ps2->push(70);
	ps2->pop();
	cout << ps2->top() << endl;
	delete ps2;  
	
	// 1、在stack段开辟内存  2、调用构造
	SeqStack ps3;
}

调用总结:
1、全局对象
全局对象定义时进行构造,程序结束才析构。
2、heap段对象
new时:1、malloc内存开辟;2、构造函数。
delete时:1、析构函数;2、free(ps2)
3、stack段对象
定义时构造,出作用域析构。

三、深拷贝浅拷贝

浅拷贝

内存的拷贝

int main() 
{
	SeqStack s1(10);
	SeqStack s2 = s1;   // #1
	SeqStack s3(s1);    // #2
	// #1和#2都是调用拷贝构造
	// #1相当于 s2.operator=(s1)
	// void operator=(const SeqStack& src)
}

在这里插入图片描述
什么时候不能浅拷贝:
对象中的成员变量指针,指向了对象外的资源。所以在浅拷贝时,两个对象的指针就指向了同一块资源

深拷贝

当对象占用外部资源时,使用深拷贝,使得其各自占有各自的资源。
在这里插入图片描述

class SeqStack
{
public:
	// 构造函数 
    SeqStack(int size = 10) {}

    // 自定义的拷贝构造函数 《= 对象的浅拷贝现在有问题了
    SeqStack(const SeqStack &src)
    {
        cout << "SeqStack(const SeqStack &src)" << endl;
        _pstack = new int[src._size];
        for (int i = 0; i <= src._top; ++i)
        {
            _pstack[i] = src._pstack[i];
        }
        _top = src._top;
        _size = src._size;
    }
    void operator= (const SeqStack &src)
    {
    	if(this == &src) return;  // 防止自赋值
    	delele[] _pstack;  // 重要,需要先释放当前对象的内存
    	
    	_pstack = new int[src.size];
    	for (int i = 0; i <= src._top; ++i)
        {
            _pstack[i] = src._pstack[i];
        }
        _top = src._top;
        _size = src._size;
	}

    // 析构函数
    ~SeqStack() {}
	
	void push(int val){}
    void pop(){}
    int top() {}
    bool empty() {}
    bool full() {}
private:
	int *_pstack;   // 动态开辟数组,存储顺序栈的元素
	int _top;		// 指向栈顶元素的位置
	int _size;		// 数组扩容的总大小
	void resize(){}
}

注意:
1、为什么拷贝时要使用for循环,而不是直接

memcpy(ptmp, _pstack, sizeof(int)*_size);

使用memcpy仍然是浅拷贝。
2、赋值构造函数时,有一个释放当前对象的内存的操作。对应shared_ptr引l用计数在赋值的时候也会减1(随即会加1)
3、赋值构造函数应该防止s1 = s1这种自赋值的情况。

编程实践

Mystring:
注意:
1、在普通构造函数中,无论如何也要开辟一块内存。保证对象是有一个有效的对象。
2、普通构造函数的输入为const char *str,因为在新版编译器中不让普通的指针指向常量字符串

char *p = "hello world";  // 不能修改*p
现在编译器都不让普通的指针指向常量字符串,应该这么写:
const char *p = "hello world";
#include <bits/stdc++.h>

using namespace std;

class String
{
public:
    String(const char *str = nullptr) 
    {
        if (str != nullptr)
        {
            m_data = new char[strlen(str) + 1];  // 加上'/0'
            strcpy(this->m_data, str);
        }
        else
        {
            m_data = new char[1]; // new char;
            *m_data = '\0'; // 0
        }
    }
    String(const String &other)
    {
        m_data = new char[strlen(other.m_data) + 1]; // 深拷贝
        strcpy(m_data, other.m_data);
    }
    ~String(void)  // 析构函数
    {
        delete[]m_data;
        m_data = nullptr;
    }
    // String& 是为了支持连续的operator=赋值操作
    String& operator=(const String &other) // 赋值重载函数
    {
        if (this == &other)
        {
            return *this;  // str1
        }

        delete[]m_data;    // 析构

        m_data = new char[strlen(other.m_data) + 1];
        strcpy(m_data, other.m_data);
        return *this; // str1
    }
private:
    char *m_data; // 用于保存字符串
};
int main()
{
    // 调用带const char*参数的构造函数
    String str1;
    String str2("hello");
    String str3 = "world";

    // 调用拷贝构造函数
    String str4 = str3;
    String str5(str3);

    // 调用赋值重载函数
    /*
    str1 = str2
    str1.operator=(str2) => str1
    str3 = str1
    */
    str3 = str1 = str2;

    return 0;
}

关于赋值重载函数中返回值为引用的情况:
如果一个方法的返回值是一个引用,那么它返回的是某个对象的引用,而不是对象本身的副本。这意味着通过引用返回的值与原始对象共享同一块内存,对返回值的修改将直接影响原始对象。
返回引用的方法有以下几个作用:

  • 避免对象的复制:通过返回引用而不是副本,可以避免在函数调用中进行大量的复制操作,提高性能。特别是对于大型对象或复杂的数据结构,避免复制可以节省时间和内存。
  • 允许链式操作:返回引用可以使多个方法调用可以连接在一起形成链式操作。这种方法通常用于实现流畅的、易读的代码,比如在输入/输出流中使用<<和>>运算符。
  • 允许对返回值进行修改:返回引用允许在函数外部对返回值进行修改。这可以用于实现函数返回某个对象的引用,以便可以直接修改该对象的状态。

需要注意的是,返回引用时需要确保返回的引用在函数调用结束后仍然有效。一般来说,应该返回指向静态、全局、或动态分配的对象的引用,而不是指向局部对象的引用,以避免出现悬垂引用(dangling references)的问题。

完整的Mystring重写见:参考链接

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【矩阵的基本操作】——MatLab基础

目录索引 矩阵的基本操作&#xff1a;转置&#xff1a;矩阵的拼接&#xff1a;*横拼&#xff1a;**竖拼&#xff1a;* 矩阵的索引&#xff1a;取元素&#xff1a;*end():* 取区域&#xff1a;逻辑判断&#xff1a;逻辑取值&#xff1a;find()&#xff1a; 矩阵的基本操作&#…

51单片机学习--独立按键控制LED

功能&#xff1a;按下K1时D1亮&#xff0c;松开时D1灭&#xff0c;P3_1对应K1 , P2_0对应D1 #include <REGX52.H>void main() {while(1) {if(P3_1 0) //按下K1{P2_0 0;}else{P2_0 1;}}} 按下按钮和松开按钮时会有抖动&#xff0c;所以需要用延时函数来避免抖动造成的…

JDK、JRE与JVM三者之间的关系及区别

文章目录 0、关系1、JDK2、JRE3、JVM 0、关系 JDK JRE Java 开发工具包 [Java,Javac,Javadoc,Javap等]JRE JVM Java 的核心类库 1、JDK 什么是JDK&#xff0c;JDK是用于Java程序开发的最小环境&#xff0c;包含&#xff1a;Java程序设计语言&#xff0c;Java虚拟机&#…

【DC-DC】APS54083 降压恒流驱动器大功率深度调光 舞台 RGB 汽车照明 台灯驱动芯片

产品描述 APS54083 是一款 PWM 工作模式,高效率、外围简单、外置功率 MOS 管&#xff0c;适用于 5-220V 输入高精度降压 LED 恒流驱动芯片。输出最大功率150W最大电流 6A。APS54083 可实现线性调光和 PWM 调光&#xff0c;线性调光脚有效电压范围 0.5-2.5V.PWM 调光频率范围 1…

手把手教你搭建SpringCloud项目(十)集成Hystrix之服务降级

什么是微服务&#xff1f;一看就会系列&#xff01; 一、手把手教你搭建SpringCloud项目&#xff08;一&#xff09;图文详解&#xff0c;傻瓜式操作 二、手把手教你搭建SpringCloud项目&#xff08;二&#xff09;生产者与消费者 三、手把手教你搭建SpringCloud项目&#x…

网络 socket

文章目录 概念和 TCP、UDP 区别和 HTTP 区别 概念 为网络通信提供的接口&#xff0c;定义了应用程序如何访问网络资源、如何发送和接收数据等&#xff0c;Socket 是一个包含了IP地址和端口号的组合&#xff0c;当一个应用程序想要与另一个应用程序通信时&#xff0c;它会向特定…

【es】部署后打不开访问页面

具体报错&#xff1a; [2023-07-18T00:55:28,203][WARN ][o.e.x.s.t.n.SecurityNetty4HttpServerTransport] [demo] received plaintext http traffic on an https channel, closing connection Netty4HttpChannel{localAddress/127.0.0.1:9200, remoteAddress/127.0.0.1:5529…

微服务Day2——Nacos注册中心入门

Nacos注册中心 1、Nacos简介 国内公司一般都推崇阿里巴巴的技术&#xff0c;比如注册中心&#xff0c;SpringCloudAlibaba也推出了一个名为Nacos的注册中心。 2、Mac安装 进入Nacos官网下载安装包 http://nacos.io/zh-cn/ Github仓库地址 下载解压后进入nacos/bin目录下 …

【力扣每日一题】2023.7.19 模拟行走机器人

题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 机器人模拟类题目,力扣里有很多这样的模拟题,就是模拟机器人在坐标系上行走. 套路就是记下每个方向行走后,x轴和y轴的变化&#xff08;代码中的direction&#xff09;,例如 direction[0] 就表示向北走一次,y轴1,x轴不变.…

回归预测 | MATLAB实现基于KELM-Adaboost核极限学习机结合AdaBoost多输入单输出回归预测

回归预测 | MATLAB实现基于KELM-Adaboost核极限学习机结合AdaBoost多输入单输出回归预测 目录 回归预测 | MATLAB实现基于KELM-Adaboost核极限学习机结合AdaBoost多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.MATLAB实现基于KELM-Adaboo…

【汉诺塔问题分析】

一、背景 汉诺塔问题是一种经典的递归问题&#xff0c;它由法国数学家Huygens在1665年发现&#xff0c;也是一道有趣的数学难题。这道问题的主要目的是将三根柱子上的一堆盘子移动到另一根柱子上&#xff0c;移动过程中每次只能移动一个盘子&#xff0c;并且大盘子不能放在小盘…

jmeter压测过程中,ServerAgent响应异常:Cannot send data to network connection

ServerAgent异常信息&#xff1a; Cannot send data to network connection&#xff08;无法将数据发送到网络连接&#xff09; 原因&#xff1a; linux 防火墙 拦截了当前端口 解决方案&#xff1a; Linux 执行以下命令 /sbin/iptables -I INPUT -p tcp --dport 4445 -j ACC…

被字节拷打了~基础还是太重要了...

今天分享一篇一位同学去字节面试的实习面经&#xff0c;技术栈是java&#xff0c;投了go后端岗位&#xff0c;主要拷打了 redismysql网络系统java算法&#xff0c;面试问题主要集中在 mysql、redis、网络这三部门&#xff0c;因为面试官是搞 go 的&#xff0c;java 只是随便问了…

Flink 启动就报错,但exception没提示。其中一个task failure 该怎么办?

文章目录 前言一、排查二、解决 前言 最近我在生产又遇到一个问题&#xff0c;就是消费着一段时间之后&#xff0c;忽然就不再消费了&#xff0c;但也不报错。观察了几次&#xff0c;我发现时间基本是停留在上下班高峰期数据量最大的时候。我主观猜测可能是同时间进来的数据过…

(学习笔记-TCP连接建立)TCP 为什么是三次握手?不是两次、四次?

常规回答&#xff1a;“因为三次握手才能保证双方具有接收和发送的能力” 原因一&#xff1a;避免历史连接 三次握手的首要原因是为了防止旧的重复连接初始化造成混乱。 假设&#xff1a;客户端先发送了SYN(seq90)报文&#xff0c;然后客户端宕机了&#xff0c;而且这个SYN报…

QGIS绘制一张地图——创建和编辑绘制线要素、由线要素生成面要素、面要素的编辑

前言 我们以描绘北京市市区案例来演示这部分功能。步骤大致如下: 1、按照市区分区的分界线来绘制线要素。 2、根据所绘线要素生成面要素。 3、对生成的面要素做整理编辑。待绘制底图如图所示: 一、创建和编辑绘制线要素 1.1 创建线要素 我们点击新建Shapefile要素按钮,…

Spring初识(一)

一.Spring 是什么&#xff1f; 首先我们来看看官网的解释 Spring 使每个人都可以更快、更轻松、更安全地进行 Java 编程。Spring 对速度、简单性和生产力的关注使其成为 世界上最受欢迎的 Java框架。 这里我简单的说明一下什么是spring? 我们通常所说的 Spring 指的是 Sprin…

以太网(Ethernet)入门了解

以太网&#xff08;Ethernet&#xff09;是一种常见的局域网&#xff08;LAN&#xff09;通信协议&#xff0c;它是由Xerox公司于1970年代中期开发的。以太网是一种基于广播技术的开放式网络协议&#xff0c;它允许设备在共享通信介质上进行通信。以下是关于以太网的基本概念、…

usb转网口转换器经常自动断网

问题&#xff1a; 最近使用一个usb转网口的扩展坞&#xff0c;发现和其它机器通信时&#xff0c;经常会自动断网。 原因&#xff1a; 和设备的电源管理策略有关&#xff0c;USB设备的“允许计算机自动关闭此设备以节约电源”选项默认是选中的&#xff0c;而网络设备的此选项默…

音视频开发实战03-FFmpeg命令行工具移植

一&#xff0c;背景 作为一个音视频开发者&#xff0c;在日常工作中经常会使用ffmpeg 命令来做很多事比如转码ffmpeg -y -i test.mov -g 150 -s 1280x720 -codec libx265 -r 25 test_h265.mp4 &#xff0c;水平翻转视频&#xff1a;ffmpeg -i src.mp4 -vf hflip -acodec copy …