C++编程(四) —— OOP

news2024/11/25 6:50:30

文章目录

  • 前言
  • 一、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重写见:参考链接

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

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

相关文章

04.MySQL——用户管理

用户管理 用户管理的价值 用户 用户信息 MySQL中的用户&#xff0c;都存储在系统数据库mysql的user表中 use mysql;select host,user,authentication_string from user;host&#xff1a; 表示这个用户可以从哪个主机登陆&#xff0c;如果是localhost&#xff0c;表示只能从…

【贪心算法Part03】| 1005.K次取反后最大化的数组和、134.加油站、135.分发糖果

目录 &#x1f388;LeetCode1005.K次取反后最大化的数组和 &#x1f388;LeetCode134.加油站 &#x1f388;LeetCode135.分发糖果 &#x1f388;LeetCode1005.K次取反后最大化的数组和 链接&#xff1a;1005.K次取反后最大化的数组和 给你一个整数数组 nums 和一个整数 k…

31,list容器

31.1list基本概念 功能&#xff1a;将数据进行链式存储 链表(list)是一种物理存储上非连续的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接实现的 链表的组成&#xff1a;链表由一系列结点组成 结点的组成&#xff1a;一个是存储数据元素的数据域&#xf…

【剧前爆米花--web】HTTP协议格式详解以及构造

作者&#xff1a;困了电视剧 专栏&#xff1a;《JavaEE初阶》 文章分布&#xff1a;这是一篇关于HTTP协议的文章&#xff0c;在这篇文章中我会说明HTTP协议格式以及相关的构造&#xff0c;希望对你有所帮助&#xff01; 目录 HTTP协议 HTTP协议格式 HTTP请求 HTTP响应详情…

拿了 7 个大厂 offer,我有话说

我有一个朋友&#xff0c;举办了一个分享会。他春招期间收割了阿里、京东、美团、拼多多、网易、腾讯云智等多家公司的实习 offer。 他最近从一家A大厂实习换到了另外一家B大厂实习&#xff0c;分享会长达100分钟&#xff0c; 共解答了 40 多个问题。 我也从中抽取一些比较有共…

一个小技巧,分分钟搞定新零售!

新零售模式的兴起带来了线上线下销售渠道的整合&#xff0c;而自动售货机作为新零售模式的一种重要形式&#xff0c;提供了便捷的自助购物体验。 自动售货机作为新零售模式的一种典型应用&#xff0c;以其便利性、快捷性和24小时无人值守的特点&#xff0c;深受消费者和商家的青…

MySQL数据库 【增删改查】

目录 一、新增 指定列插入 一次插入多个数据 二、查询 1、全列查询 2、指定列查询 3、查询字段为表达式 4、查询的时候给列名/表达式 指定别名 5、查询时去重 6、排序查询 7、条件查询 8、模糊查询 9、空值查询 10、分页查询 三、修改 四、删除 SQL 最核心…

python如何知道你的导包在哪/site-package在哪/anaconda中的模块文件在哪

参考: https://stackoverflow.com/questions/31003994/where-is-site-packages-located-in-a-conda-environment anaconda虚拟环境中的site-package在如下目录&#xff0c;/opt/conda/envs/env_cp37_STAGATE_TF/lib/python3.7/site-packages/。 基于寻找你导包的物理位置在哪…

婚纱照主题定义

文章目录 前言关于我们听我们说关于风格&#xff1a;1、主纱&#xff08;内&#xff09;2、工笔画&#xff08;内&#xff09;3、个性潮拍 &#xff08;内&#xff09;4、森系&#xff08;外&#xff09;5、园林&#xff08;外&#xff09;写在最后 前言 非常感谢在今生有约婚…

doris恢复库恢复表

今天眼疾手快 不小心删了公司生产环境的表 而且碰巧这个数据没有备份的 当时哥们就呆住 还好doris升级过1.2 刚推出了恢复数据的功能~~~~~这里给老天爷磕一个了~~~~~~ 数据删除恢复 Doris为了避免误操作造成的灾难&#xff0c;支持对误删除的数据库/表/分区进行数据恢复&…

什么是DIN连接器 DIN连接器的应用

一、什么是DIN连接器 DIN连接器是一种遵循德国国家标准化组织制定的连接器标准的电子连接器。它采用圆形外观和标准化的接口设计&#xff0c;以确保与其他符合DIN标准的设备和连接器的兼容性。DIN连接器通常由插头和插座两部分组成&#xff0c;通过插拔操作实现电路的连接和断开…

面试时被问到职业规划,怎样回答才最加分?

很多人被问到「职业规划」这个问题的时候&#xff0c;都会很………纠结&#xff1a; 表现得太踏实苦干了&#xff0c;可能会让人觉得没有什么进取心。 职业目标描述得太明确&#xff0c;又会被质疑太在意外部激励而缺乏内部动机。 感觉怎么说都会错&#xff0c;说多一句都是…

Java 中 synchronized 的优化操作:锁升级、锁消除、锁粗化

由 并发编程中常见的锁策略 总结可知&#xff0c;synchronized 具有以下几个特性&#xff1a; 开始时是乐观锁&#xff0c;如果锁冲突频繁&#xff0c;就转换为悲观锁。开始是轻量级锁实现&#xff0c;如果锁被持有的时间较长&#xff0c;就转换成重量级锁。实现轻量级锁时&am…

预处理详解(二)---#define 定义宏 + 宏的使用 + 宏和函数的区别

文章目录 #define 定义标识符#define 定义宏#define 的替换规则带副作用的宏参数宏和函数的区别#undef 的作用冷门知识点&#xff1a;#与## #define 定义标识符 #define定义标识符的格式如下&#xff1a; #define MAX 100 #define reg register//懒人觉得register太长了这些被…

Virus Total 曝数据泄露大事件:涉及多国情报部门

The Hacker News 网站披露&#xff0c;可疑文件和病毒在线检测平台 VirusTotal 曝出数据泄露事故&#xff0c;一名员工无意中将部分 VirusTotal 注册客户的姓名、电子邮件地址等敏感数据信息上传到了恶意软件扫描平台&#xff0c;此举导致约 5600 名用户数据泄露。 据悉&#x…

Display

Pipeline Dataloader和后面网络训练是解耦的&#xff0c;Dataloader负责把数据读出来变成tensor&#xff0c;网络&#xff08;继承nn.Module父类&#xff09;负责把这tensor算成最后的输出。在网络传播的过程中&#xff0c;hook记录保留中间数据&#xff0c;用于display作图。…

CSS——基础知识及使用

CSS 是什么 CSS是层叠样式表 (Cascading Style Sheets)的简写.CSS 能够对网页中元素位置的排版进行像素级精确控制, 实现美化页面的效果. 能够做到页面的样式和结构分离。 基本语法规范 选择器 { 一条/N条声明 } 选择器决定针对谁修改 (找谁)声明决定修改啥. (干啥)声明的…

PP速度模式应用

应用场景要求&#xff1a; 电机在变化的负载下&#xff0c;保持设定的速度时&#xff0c;需要使用速度模式。 在速度模式下&#xff0c;电机速度由发送到电机的电压控制。但是要改变电机的速度&#xff08;加速或减速&#xff09;需要增加或减小电机转矩&#xff0c;因此在速度…

Stable Diffusion - 编辑生成 (OpenPose Editor) 相同人物姿势的图像

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/131818943 OpenPose Editor 是 Stable Diffusion 的扩展插件&#xff0c;可以自定义人物的姿势和表情&#xff0c;以及生成深度、法线和边缘图等信…