如何在 C++ 模板容器中实现 erase() 方法

     2023-02-21     237

关键词:

【中文标题】如何在 C++ 模板容器中实现 erase() 方法【英文标题】:How to implement erase() method in a C++ template container 【发布时间】:2015-05-09 18:47:44 【问题描述】:

对于书本练习,我需要创建一个名为 Vec 的简单 C++ 容器(模仿 std::vector 的容器)。但是,我在实现一个简单的 erase() 方法时遇到了问题。我的意图是销毁该索引中的对象,然后将索引之后的所有元素移回列表的下方。

PS:我是 C++ 新手,对 C++ 中的内存管理知之甚少。

Vec.h

#pragma once
#ifndef GUARD_VEC_H
#define GUARD_VEC_H

#define WARNING D_SCL_SECURE_NO_WARNINGS
#define WARNING_ID 4996

#pragma message (WARNING)
#pragma warning (disable: WARNING_ID)

#define MAX(a , b) ((a > b) ? a : b) 
#define MIN(a , b) ((a < b) ? a : b)

#include <memory> //For: Allocator
#include <cstddef> //FOR: size_type


template<class T> class Vec 
    public: //interface
        typedef T* iterator;
        typedef const T* const_iterator;
        typedef size_t size_type;
        typedef T value_type;

        Vec()  create(); 
        explicit Vec(size_type n, const T& val = T())  create(n, val); 
        Vec(const Vec& v)  create(v.begin(), v.end()); 

        Vec& operator = (const Vec&);
        ~Vec()  uncreate(); 
        T& operator[] (size_type i)  return data[i]; 
        const T& operator[] (size_type i) const  return data[i]; 

        void push_back(const T& t) 
            if (avail == limit)
                grow();
            unchecked_append(t);
        

        void clear();
        bool erase(size_type i);

        size_type size() const  return avail - data; 

        iterator begin()  return data; 
        const_iterator begin() const  return data; 

        iterator end()  return avail; 
        const_iterator end() const  return avail;  
    private: //implementation
        iterator data;
        iterator avail;
        iterator limit;

        std::allocator<T> alloc;

        void create();
        void create(size_type, const T&);
        void create(const_iterator, const_iterator);

        void uncreate();

        void grow();
        void unchecked_append(const T&);
;

template <class T> bool Vec<T>::erase(size_type i)
    //No, doesn't work at all
    if (i > size())
        return false;
    alloc.destroy(data+ i);
    return true;
    //Implement move-back



template <class T> void Vec<T>::clear()
    uncreate(); //Destroys all objects and deallocates all addresses
    grow(); //Allocates addresses for next use.


template <class T> void Vec<T>::create()

    data = avail = limit = 0;


template <class T> void Vec<T>::create(size_type n, const T& val)

    data = alloc.allocate(n);
    limit = avail = data + n;
    std::uninitialized_fill(data, limit, val);


template <class T>
void Vec<T>::create(const_iterator i, const_iterator j)
    data = alloc.allocate(j - i);
    limit = avail = std::uninitialized_copy(i, j, data);


template <class T> void Vec<T>::uncreate()

    if (data)
        iterator it = avail;
        while (it != data)
            alloc.destroy(--it);
        alloc.deallocate(data, limit - data);
    
    data = limit = avail = 0;


template <class T> void Vec<T>::grow()

    size_type new_size = MAX(2 * (limit - data), ptrdiff_t(1));
    iterator new_data = alloc.allocate(new_size);
    iterator new_avail = std::uninitialized_copy(data, avail, new_data);

    uncreate();

    data = new_data;
    avail = new_avail;
    limit = data + new_size;


template <class T> void Vec<T>::unchecked_append(const T& val)

    alloc.construct(avail++, val);


template <class T> Vec<T>& Vec<T>::operator=(const Vec<T>& rhs)
 //rhs = right hand side
    if (&rhs != this)
        uncreate();
        create(rhs.begin(), rhs.end());
    
    return *this;

#endif

ma​​in.cpp

#include "Vec.h"
#include <iostream>
#include "Windows.h"
#include <string>

int main()
    Vec<double> vector;
    for (int i = 0; i < 10; i++)
        vector.push_back((double) i);
    

    //Check Copy Constructor
    Vec<double> vector2 = vector;



    Vec<double> vector3;
    Vec<double> vector4;
    for (int i = 0; i < 10; i++)
        vector3.push_back((double)(i*2));
    

    //Check Assignment Operator
    vector4 = vector3;



    for (int i = 0; i < 10; i++)
        std::cout << vector[i] << " " << vector2[i] << " " << " " << vector3[i] << " "
            << vector4[i] << std::endl;
    

    //Check Erase Operator
    vector.erase(3);

    for (int i = 0; i < vector.size(); i++)
        std::cout << vector[i] << std::endl;
    

    std::system("PAUSE");
    return 0;

主要,“矢量”是我正在测试的对象。它被初始化为数字 0 到 9,并且在调用 vector.erase(3) 后保持不变;

另外,关于迭代器的解释,data 指向列表的开头(第一个元素),avail 指向初始化元素的结束位置,limit 指向未初始化存储结束的位置。数据

【问题讨论】:

附带问题:MAX 不需要宏。使用std::max 或自己编写一个模板函数,但避免使用宏。 编译器错误?意外行为?介意explain more? 没有错误,只有矢量是“不变的” 仅仅因为你破坏了对象并不意味着它从向量中消失了。同样的原因你仍然可以在SomeClass c; c.~SomeClass(); 之后访问c(但不要那样做)。 【参考方案1】:

这似乎是家庭作业,所以这不是一个完整的答案。

不过我会给出提示。

您最初对解决方案的猜测存在很大问题。一旦您销毁了data[i],该位置现在(或应该被视为)不能用作T 类型的对象。特别是不能使用data[i] = data[i+1],因为赋值运算符的知识可能已经被破坏了。

标准补救措施适用:那就不要那样做。

您已经发现需要将元素 i 上方的每个元素向下移动一个。所以在你摧毁任何东西之前,先这样做。 (这是一个大提示。)当你完成后,很可能有一些东西需要销毁(另一个大提示)。

顺便说一句,我上面暗示的解决方案不是很有效。在 C++11 中有更有效的方法来做到这一点。

【讨论】:

这段代码可以作为另一种解决方案吗?它似乎可以完成这项工作,但不确定是否存在任何背景问题。 avail = std::uninitialized_copy(data + index, avail, data + index-1);【参考方案2】:

这是我如何让擦除功能工作的:

template <class T> bool Vec<T>::erase(size_type i)
    
        if (i > size())
            return false;
        //alloc.destroy(data + i); => not necessary, assuming T has a proper assignment operator defined

        for (int i = 0; i < size() - 1; i++)
            data[i] = data[i + 1];

        avail--; //decrease the size of the vector
        alloc.destroy(data + size());
        return true;
    

您可能想查看Difference between "destroy" "destructor" "deallocate" in std::allocator? 以了解 allocator.destroy 的作用。

【讨论】:

这不是答案。它调用未定义的行为。一旦销毁,语句data[i] = data[i+1] 会调用未定义的行为。 我没有说拷贝构造函数。我解决了您对data[i] = data[i+1] 的使用问题。一旦 data[i] 被销毁,这是未定义的行为。 更新后的解决方案还是有一些问题。 (1) 使用data[i]=data[i+1] 对于超出向量可用部分末尾的元素是无效的(未定义的行为)。你的循环太高了。 (2) 如果你解决了这个问题,这种方法可能会泄漏资源。向量的最后一个可用元素 (data[avail-1]) 将在其上方有一个副本。

java示例代码_在C++模板中实现java泛型自界说类型的等价

java示例代码_在C++模板中实现java泛型自界说类型的等价 查看详情

你如何在 C++ 中实现阶乘函数? [复制]

】你如何在C++中实现阶乘函数?[复制]【英文标题】:HowdoyouimplementthefactorialfunctioninC++?[duplicate]【发布时间】:2011-08-0823:19:27【问题描述】:可能的重复:CalculatinglargefactorialsinC++Howtocomputethefactorialofx如何在C++中实现阶乘函数?我... 查看详情

如何在 C++ 中实现接口? [复制]

】如何在C++中实现接口?[复制]【英文标题】:howtoimplementInterfacesinC++?[duplicate]【发布时间】:2012-04-0303:42:42【问题描述】:可能重复:PreferredwaytosimulateinterfacesinC++我很想知道C++中是否有接口,因为在Java中,设计模式的实现主要... 查看详情

如何在 C++ 中实现函数超时

】如何在C++中实现函数超时【英文标题】:Howtoimplementtimeoutforfunctioninc++【发布时间】:2017-03-2520:18:51【问题描述】:我有函数f;我想在启动f后抛出异常1s。我无法修改f()。用c++可以吗?tryf();catch(TimeoutException&e)//timeout【问题讨... 查看详情

如何在 C++ 中实现序列化

】如何在C++中实现序列化【英文标题】:HowtoimplementserializationinC++【发布时间】:2010-12-2101:08:45【问题描述】:每当我发现自己需要在C++程序中序列化对象时,我都会使用这种模式:classSerializablepublic:staticSerializable*deserialize(istrea... 查看详情

如何在头文件C++中实现类对象

】如何在头文件C++中实现类对象【英文标题】:HowtoimplementclassobjectsinheaderfileC++【发布时间】:2019-07-2406:12:47【问题描述】:如何在头文件中实现类对象,以便每次包含头文件时,都可以访问cpp文件中的对象?这是我现在的代码... 查看详情

如何使用 OpenMP 在 C++ 中实现监视器?

】如何使用OpenMP在C++中实现监视器?【英文标题】:HowtoimplementamonitorinC++usingOpenMP?【发布时间】:2017-10-2013:20:06【问题描述】:我正在使用C++编写Producer-consumerproblem,使用OpenMP时实现监视器的方法是什么?我在OpenMP中找不到像con... 查看详情

如何在 C++ 中实现这个数据结构

】如何在C++中实现这个数据结构【英文标题】:Howtoimplementthisdatastructureinc++【发布时间】:2019-03-0121:31:51【问题描述】:我正在从C#/Java迁移到C++,并且无法正确地了解做事的方法。我正在构建一种数据结构来支持算法和功能。... 查看详情

在 C++ 中实现归并排序

...8-1508:54:51【问题描述】:我研究过归并排序的理论,但对如何在C++中实现它一无所知。我的问题是,合并排序以递归方式创建数组。但是在实现的时候,我们如何在运行时创建数组呢?或者对此的一般方法是什么?谢谢。【问题... 查看详情

如何在 C++ 中实现强大的数据持久层?

】如何在C++中实现强大的数据持久层?【英文标题】:HowdoIimplementarobustDataPersistenceLayerinC++?【发布时间】:2016-07-2506:51:56【问题描述】:我第一次用C++为我的公司创建一个巨大的程序,我想创建一个很好的模式来连接到我的MySql... 查看详情

如何在 C++ 中实现这个结果?指向数组的数组

】如何在C++中实现这个结果?指向数组的数组【英文标题】:HowcanIachievethisresultinc++?Arrayspointingtoarrays【发布时间】:2015-02-0810:14:31【问题描述】:我正在尝试使用c/c++执行以下操作。为了解释代码应该如何工作,我编写了这个示... 查看详情

如何在 VIsual C++ 2010 中实现 C++ 原始字符串文字?

】如何在VIsualC++2010中实现C++原始字符串文字?【英文标题】:HowtoimplementC++rawstringliteralsinVIsualC++2010?【发布时间】:2012-12-2303:12:58【问题描述】:您可能已经知道,C++11中的新字符串文字可以以非常灵活的方式表示。R"&lt;d... 查看详情

如何在电容器中实现 ios 和 android google 登录?

】如何在电容器中实现ios和androidgoogle登录?【英文标题】:howtoimplementios&androidgoogleloginincapacitor?【发布时间】:2022-01-0604:34:48【问题描述】:我正在使用电容器-firebase-auth来设置我的应用程序。已经按照以下指南进行设置,... 查看详情

如何在 jdbc 模板中实现 RowMapper

】如何在jdbc模板中实现RowMapper【英文标题】:HowtoimplementRowMapperinjdbctemplate【发布时间】:2016-07-0108:47:01【问题描述】:我有一个查询,它给出以下输出:StateCSTQTYaa010aa120aa230aa340ac010ac120ac230ac340我在springmvc中使用jdbc模板:在实现... 查看详情

如何在django模板中实现列表迭代[重复]

】如何在django模板中实现列表迭代[重复]【英文标题】:HowtoachievelistIterationindjangotemplate[duplicate]【发布时间】:2012-12-2401:00:43【问题描述】:可能重复:Getlistitemdynamicallyindjangotemplates如何获取具有可变计数器的列表项。目的是获... 查看详情

如何在 C++ 和 FLTK 中实现倒计时时钟?

】如何在C++和FLTK中实现倒计时时钟?【英文标题】:HowtoimplementacountdownclockinC++andFLTK?【发布时间】:2015-08-1601:58:09【问题描述】:我使用ProgrammingwithC++中的FLTK和Gui库创建了一个小游戏,我想使用倒计时时钟计时器。FLTK有Fl::add_t... 查看详情

如何在 Android Studio 的登录活动模板中实现 AsyncTask

】如何在AndroidStudio的登录活动模板中实现AsyncTask【英文标题】:HowtoimplementAsyncTaskinLoginActivitytemplatefromAndroidStudio【发布时间】:2019-09-1105:07:14【问题描述】:我想在我的android应用中实现一个登录活动,我使用AndroidStudio模板进行... 查看详情

如何在 Django 模板中实现面包屑?

】如何在Django模板中实现面包屑?【英文标题】:HowtoimplementbreadcrumbsinaDjangotemplate?【发布时间】:2010-10-2400:52:22【问题描述】:在Google搜索“Django面包屑”时提供的一些解决方案包括使用模板和block.super,基本上只是扩展基本块... 查看详情