C++中的new和模版

news2024/9/21 2:33:30

前言

        随着C++的学习,讲了C++的发展过程、流插入、流提取、函数缺省值、类与构造等等。接下来学习C++很方便的 玩意,函数模版。函数模版就像是模具一样,C++会自动用模版编译出合适的函数供程序员使用。以前不同类型相同操作的函数都能通过函数模版,只写一个来解决。这么说来,怪不得祖师爷会写出函数重载这样的优化。因为如此,函数模版大有作用。

        在此之前,上一节中提到的新增操作符new和delete。这两个操作符也是在堆上申请空间操作符,这一节会回顾一下,并进行补充。

一、new和delete

1、简介

        new和delete是C++中新增的关键字,new用来申请堆上的空间,delete用来释放堆上的空间。在C语言中我们学过了3个函数,分别是“malloc”“ralloc”“free”,前两个用来申请空间,free用来释放空间。

        既然C语言中已经有了申请空间的函数,为什么C++中又单独开发出来一套系统来申请空间呢?接下来就来聊聊他们之间的区别吧。

2、malloc/free与new/delete的区别:

(1)malloc/free是函数,new/delete是操作符。

(2)malloc申请空间就只是申请空间不会初始化,new会申请空间并初始化空间。

(3)malloc需要手动计算申请空间的大小,new能够通过类型自动计算需要申请大小。

(4)malloc申请空间失败会返回NULL,new申请空间失败不会返回NULL但是需要捕获异常(这里需要用到try/catch)。

(5)malloc只是开空间,free只会释放空间、new会调用构造函数初始化空间,delete会调用析构函数释放空间。

3、实际举例

        如果正常的使用new和delete,也就是说配套使用,方法如下:

#include <iostream>

using namespace std;

class A
{
public:
    A(int a = 0)
        :_a(a)
    {
        cout << "A(int a = 0)->" << _a <<endl;
    }

    ~A()
    {
        cout << "~A()->" << _a << endl;
    }
private:
    int _a;
};

int main()
{
    A* exp = new A;
    delete exp;

    exp = new A[4]{1,2,3,4};
    delete[] exp;
    exp = nullptr;
    return 0;
}

        如举例所示,在new后面接上“类型”就能够直接开辟一个类的空间,释放空间接delete和它的指针。如果需要开辟多个空间就像数组一样在new后面接上“类型[]”,释放空间就需要接“delete[]”和它的指针。同时如果想修改初始化中的成员可以增加“{}”,里面写变量。那么调用构造的时候会直接使用。

        但是为什么不直接用“delete”而是需要增加“delete[]”?这是因为用new申请多个空间的时候会在所开空间前额外开4个字节的空间用来记录调用构造和析构函数的次数,方便处理。这个时候使用delete会出错,程序会直接结束。因为多开了4字节。到了现在C++的进步,如果不写析构函数,那么优化做的比较多的编译器就不会多生成这4个字节“delete”也能够使用。

        如果将类“A”的析构函数去掉,那么使用“delete”释放空间也是可行的。例如:

class A
{
public:
    A(int a = 0)
        :_a(a)
    {
        cout << "A(int a = 0)->" << _a <<endl;
    }

private:
    int _a;
};

        另外,malloc/free也可以和new/delete混用,但是不会调用构造和析构函数,如果类里面有申请空间,就不能使用例如:

class B
{
public:
    B()
    {
        _b = new int(0);
    }

    ~B()
    {
        delete _b;
    }
private:
    int* _b;
};

        这是因为malloc/free不会调用析构和构造去释放申请的空间,造成内存泄漏,所以最好将new和delete配套使用。

4、练习题举例

求1+2+3+...+n_牛客题霸_牛客网求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、swit。题目来自【牛客题霸】icon-default.png?t=N7T8https://www.nowcoder.com/practice/7a0da8fc483247ff8800059e12d7caf1?tpId=13&tqId=11200&tPage=3&rp=3&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

        描述

        求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

        数据范围: 0<𝑛≤2000<n≤200
        进阶: 空间复杂度 𝑂(1)O(1) ,时间复杂度 𝑂(𝑛)O(n)

        这道题就能用到我们类的算法,建立全局变量然后通过类的创建来修改它,只需要用new创建n个类的空间,然后自动调用构造函数进行累加,这样就能计算出和:

class Sum
{
public:
    Sum()
    {
        i++;
        ret += i;
    }

    static void Reset()
    {
        i = 0;
        ret = 0;
    }

    static int i;
    static int ret;
};

int Sum::i = 0;
int Sum::ret = 0;

class Solution {
public:
    int Sum_Solution(int n) {
        Sum::Reset();
        Sum* ps = new Sum[n];
        delete[] ps;
        return Sum::ret;
    }
};

二、模版

1、函数模版

1.1、简介

        函数模板不是一个实在的函数,编译器不能为其生成可执行代码。定义函数模板后只是一个对函数功能框架的描述,当它具体执行时,将根据传递的实际参数决定其功能。

        它的出现虽然对于编译器来说增加了开销,但是却节省了程序员的开销,让程序员写一个功能就能一劳永逸。

1.2、举例使用

        在讲到C语言函数的时候,我们学到的有加法函数,就是将两个数加起来,最后变成一个数返回。那么这里我们也用这个函数举例。

template<class T>
T Add(T& n1, T& n2)
{
    return n1 + n2;
}

        这里的“tempelate”是C++中新增的关键字,这个关键字能够用来定义模版变量的类型。在上述函数中,“n1”和“n2”是同一种类型,他们都属于类型“T”。这里的“T”可以根据需要取名字,就和类一样。这个类型“class”也可以使用“typename”,所以这个函数用以下写法也是一样的:

template<typename T1>
T1 Add(T1& n1, T1& n2)
{
    return n1 + n2;
}

        在函数的使用方面分为两种方法,第一种是推导实例化、另一种是显示实例化。接下来会在例子中主要说明两种实例化的不同操作方式。模版函数就采用上面写的“Add”函数:

#include <iostream>
using namespace std;

template<class T>
T Add(T n1, T n2)
{
    return n1 + n2;
}

int main()
{
    int a1 = 10, a2 = 20;
    double d1 = 10.1, d2 = 20.2;

    // 推导实例化
    cout << Add(a1, a2) << endl;
    cout << Add(a1, (int)d1) << endl;
    cout << Add((double)a1, d1) << endl << endl;

    // 显示实例化
    cout << Add<int>(a1, d1) << endl;
    cout << Add<double>(a1, d1) << endl;

    return 0;
}

        推导实例化就是模版的参数程序员不给出,由传入函数的参数以及编译器的推导决定。相反显示实例化就是程序员固定了模版的类型,计算机直接将这个类型交给模版就行了。注意返回值无法影响编译器的推导,只有输入的形参才行。上述代码的结果如下:

        请注意,如果使用推导实例化就不能让编译器搞不清楚变量实例化成什么类型。如下传法都是错误的:

int main()
{
    int a1 = 10, a2 = 20;
    double d1 = 10.1, d2 = 20.2;

    // 推导实例化
    cout << Add(a2, d2) << endl;
    cout << Add(a1, d1) << endl;
    cout << Add(a1, d2) << endl << endl;

    return 0;
}

        编译器会觉得,到底是“int”还是“double”呢?就直接报错退出了。所以这里建议使用显示实例化,编译器也不含糊,不容易出错。

2、类模版

        除了函数能够使用模版之外,类也能使用模版。他们的作用都是相同的,减少程序员的代码量。使用方法也类似。所以这里就直接举例了。

2.1、使用举例

        比如说我们需要建立一个可以装不同数据的栈,那么我们也可以用到函数模版:

namespace lcs
{
    template<typename T>
    class Stack
    {
    public:
        Stack(int n = 4)
            :_array(new T[n])
            ,_capacity(n)
            ,_size(0)
        {}

        ~Stack()
        {
            delete[] _array;
            _array = nullptr;
            _size = _capacity = 0;
        }

        void push(const T& x)
        {
            if(_size == _capacity)
            {
                T* tmp = new T[_capacity * 2];
                memcpy(tmp, _array, sizeof(T) * _size);
                delete[] _array;
                _array = tmp;
                _capacity *= 2;
            }
            _array[_size++] = x;
        }

        void pop()
        {
            if(_size != 0)
            {
                --_size;
            }
        }

        T top()
        {
            if(_size != 0)
            {
                return _array[_size - 1];
            }
            else{
                return T(0);
            }
        }

        bool empety()
        {
            return !_size;
        }

    private:
        T* _array;
        size_t _capacity;
        size_t _size;
    };
}

int main()
{
    lcs::Stack<int> st_int;
    lcs::Stack<double> st_double;

    st_int.push(1);
    st_int.push(2);
    st_int.push(3);
    st_int.push(4);

    cout << st_int.top() << endl;

    st_int.pop();
    st_int.pop();
    st_int.pop();

    cout << st_int.top() << endl;

    st_double.push(1.123);

    cout << st_double.top() << endl;

    return 0;
}

        这样我们的栈也能够使用了,和函数不同的是类模板都需要显示实例化,请注意。

        这里我也用到了命名空间,这样就不会和库里的函数重叠。运行后无误:

        创建了两种栈“int”和“double”的,同时输入输出一些栈内元素,输出上和代码预期相同。首先入4个数到“int”栈,然后取栈顶元素输出。然后从“int”栈中删除3个再次输出栈顶元素。随后输入一个元素到“double”栈,然后输出它。

        在模版之中的类型也可以是自定义类型,但相应的就需要写更多的代码来确保运行。

作者结语

        到这里C++中的模版也就告一段落了,学习本节我觉得最大的作用就是为接下来学习stl标准库做准备,当然标准库的介绍分一节还讲不完。接下来可能会分多次博客进行细致讲解,或者偷个懒,全部写完总结之后写到一篇里一次发出来。又是一项大工程了,吐血。

        要是博客也有个模版就好了,我也一套然后计算机帮我整理,哈。我愿称为计算机万能。接下来其实还挺简单的,虽然我还没学到但是我预习了。和这里模版类中自己写的类差不多的写法就行。之前也学过很多操作符重载,就是这些。然后就是通用的一些函数,包装一下就能直接用了。

         除了string还要重新学习C++的顺序表和链表和二叉树和哈希表。我故意这么写的,主要是想起来还很多。干啦兄弟们!

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

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

相关文章

【iOS】——内存对齐

内存对齐是什么 内存对齐指的是数据在内存中的布局方式&#xff0c;它确保每个数据类型的起始地址能够满足该类型对齐的要求。这是因为现代处理器在访问内存时&#xff0c;如果数据的起始地址能够对齐到一定的边界&#xff0c;那么访问速度会更快。这种对齐通常是基于数据类型…

客户中心应急管理的作用和特征

近些年作为事故、灾难等风险的预防主体和第一响应者&#xff0c;客户中心的应急管理取得了较大进展&#xff0c;但总体上仍存在很多薄弱环节&#xff0c;如安全事故频发&#xff0c;自然灾害、公共卫生、社会安全事件等给运营机构带来了多方面的不利影响。从信息角度看&#xf…

20240720 每日AI必读资讯

OpenAI 推出GPT-4o mini取代 GPT 3.5&#xff01; - 性能超越 GPT 4&#xff0c;而且更快更便宜 - 该模型在MMLU上得分为82%&#xff0c;在LMSYS排行榜上的聊天偏好测试中表现优于GPT-4。 - GPT-4o mini的定价为每百万输入标记15美分和每百万输出标记60美分&#xff0c;比之…

【golang-ent】go-zero框架 整合 ent orm框架 | 解决left join未关联报错的问题

一、场景 1、子表&#xff1a;cp_member_point_history cp_member_point_history表中字段&#xff1a;cp_point_reward_id 是cp_point_reward的主键id 当本表中的cp_point_reward_id字段为0&#xff08;即&#xff1a;没有可关联主表的&#xff09; CREATE TABLE cp_member_poi…

项目开发之文件上传 (秒传、断点续传、分片上传)(看这一篇就懂了)

目录&#xff1a; 前言秒传什么是秒传核心逻辑代码实现 小文件上传什么是小文件上传核心逻辑代码实现 分片上传什么是分片上传核心逻辑代码实现 断点续传什么是断点续传核心代码实现 前言 文件上传在项目开发中再常见不过了&#xff0c;大多项目都会涉及到图片、音频、视频、文…

npm安装依赖包报错,npm ERR! code ENOTFOUND

一、报错现象&#xff1a; npm WARN registry Unexpected warning for https://registry.npmjs.org/: Miscellaneous Warning ETIMEDOUT: request to https://registry.npmjs.org/vue failed, reason: connect ETIMEDOUT 104.16.23.35:443 npm WARN registry Using stale data…

Python | Leetcode Python题解之第235题二叉搜索树的最近公共祖先

题目&#xff1a; 题解&#xff1a; class Solution:def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:ancestor rootwhile True:if p.val < ancestor.val and q.val < ancestor.val:ancestor ancestor.leftelif p.val >…

【力扣】最小栈

&#x1f525;博客主页&#xff1a; 我要成为C领域大神&#x1f3a5;系列专栏&#xff1a;【C核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 设计一个支持 push…

object-C 解答算法:合并两个有序数组(leetCode-88)

合并两个有序数组(leetCode-88) 题目如下图:(也可以到leetCode上看完整题目,题号88) 首先搞懂,什么叫“非递减顺序” 非递减顺序,是指一个序列中的元素从前往后&#xff08;或从左到右&#xff09;保持不减少或相等。 这意味着序列中的元素可以保持相同的值&#xff0c;但不会…

c++ pc输入法例子

1、微软开源demo Windows-classic-samples/Samples/IME at master jiangdon2007/Windows-classic-samples (github.com) 2、打开SampleIME.sln 编译【32位或者64位】 3、将SampleIME.dll 和SampleIMESimplifiedQuanPin.txt 放在同一个目录 4、注册 regsvr32 SampleIME.dl…

spring 5.3.x 、6.1.x、6.0.x 源码本地编译运行

参考大佬文章&#xff0c;完美完成本地idea spring源码编译和demo测试 参考链接&#xff08;spring5.3.x&#xff09; spring5.3.x源码阅读环境搭建 下面是spring6.0.x参考 spring6.0.x jdk调成17 idea 2022.2.4版本本地编译spring源码项目 spring6.0.x 分支 gradle-8…

小程序-4(自定义组件:数据、属性、数据监听器、生命周期函数、插槽、父子通信、behaviors)

目录 1.组件的创建和引用 局部引用组件 全局引用组件 组件和页面的区别 组件样式隔离 ​编辑 组件样式隔离的注意点 修改组件的样式隔离选项 data数据 methods方法 properties属性 data和properties属性的区别 使用setData修改properties的值 2.数据监听器 什么…

stm32入门-----EXTI外部中断(下——实践篇)

目录 前言 一、硬件介绍 1.对射红外线传感器 2.旋转编码器 二、EXTI外部中断C编程 1.开启RCC时钟 2.配置GPIOK口初始化 3.配置AFIO 4.配置EXIT 5.配置NVIC 三、EXIT外部中断项目实操 1.对射红外传感器计数 2.选择编码器计数 前言 本期接着上一期的内容继续学习stm3…

AutoMQ 生态集成 Redpanda Console

通过 Kafka Web UI 更加便利地管理 Kafka/AutoMQ 集群 随着大数据技术的飞速发展&#xff0c;Kafka 作为一种高吞吐量、低延迟的分布式消息系统&#xff0c;已经成为企业实时数据处理的核心组件。然而&#xff0c;Kafka 集群的管理和监控却并非易事。传统的命令行工具和脚本虽…

Java流的概念及API

流的概念 流&#xff08;Stream)的概念代表的是程序中数据的流通&#xff0c;数据流是一串连续不断的数据的集合。在Java程序中&#xff0c;对于数据的输入/输出操作是以流(Stream)的方式进行的。可以把流分为输入流和输出流两种。程序从输入流读取数据&#xff0c;向输出流写入…

Python项目打包与依赖管理指南

在Python开发中&#xff0c;python文件需要在安装有python解释器的计算机的电脑上才能运行&#xff0c;但是在工作时&#xff0c;我们需要给客户介绍演示项目功能时并不一定可以条件安装解释器&#xff0c;而且这样做非常不方便。这时候我们可以打包项目&#xff0c;用于给客户…

《驾驭AI浪潮:伦理挑战与应对策略》

AI发展下的伦理挑战&#xff0c;应当如何应对&#xff1f; 人工智能飞速发展的同时&#xff0c;也逐渐暴露出侵犯数据隐私、制造“信息茧房”等种种伦理风险。随着AI技术在社会各个领域的广泛应用&#xff0c;关于AI伦理和隐私保护问题日趋凸显。尽管国外已出台系列法规来规范…

达梦数据库DM8-索引篇

目录 一、前景二、名词三、语法1、命令方式创建索引1.1 创建索引空间1.2.1 创建普通索引并指定索引数据空间1.2.2 另一种没验证&#xff0c;官方写法1.3 复合索引1.4 唯一索引1.5 位图索引1.6 函数索引 2、创建表时候创建索引3、可视化方式创建索引3.1 打开DM管理工具3.2 找到要…

nginx负载均衡实例

实现效果 浏览器输入地址http://nginx服务器ip(:80)/edu/a.html&#xff0c;实现负债均衡效果&#xff0c;平均分配到 服务器ip:8080和 服务器ip:8081进程中。 准备工作 准备两个tomcat&#xff0c;一个监听在8080端口&#xff0c;一个监听在8081端口。也可以准备多个tomcat。…

如何在电脑上演示手机上APP,远程排查移动端app问题

0序&#xff1a; 对接客户&#xff0c;给领导演示移动端产品&#xff0c;或者远程帮用户排查移动端产品的问题。都需要让别人能够看到自己在操作手机。 会议室可以使用投屏&#xff0c;但需要切换电脑和手机。 排查问题经常都是截图、或者手机上录制视频&#xff0c;十分繁琐…