C++ 在dynamicDataHolder | C++;

C++ 在dynamicDataHolder | C++;,c++,C++,运行此程序并在main()中输入值:后,我得到错误“检测到堆损坏”: 我已经花了大约一个小时试图找出是什么原因导致了这一点,希望大家能帮助我解决这一问题,以及我需要做些什么来解决这一问题 谢谢 #include <iostream> using namespace std; template <class t> class dynamicDataHolder { t* ptr = NULL; int size = 0; int elements

运行此程序并在
main()
中输入
值:
后,我得到错误“检测到堆损坏”:

我已经花了大约一个小时试图找出是什么原因导致了这一点,希望大家能帮助我解决这一问题,以及我需要做些什么来解决这一问题

谢谢

#include <iostream>

using namespace std;

template <class t>
class dynamicDataHolder
{
    t* ptr = NULL;
    int size = 0;
    int elements = 0;

public:

    dynamicDataHolder() {
        ptr = new t[5];
        size = 5;
        elements = 0;
    }

    dynamicDataHolder(int arraySize) {
        ptr = new t[size];
        size = arraySize;
        elements = 0;
    }

    dynamicDataHolder(const dynamicDataHolder<t>& obj) {
        ptr = new t[obj.size];

        if (ptr != NULL) {
            for (int i = 0; i < obj.size; i++) {
                ptr[i] = obj.ptr[i];
            }
        }
        else {
            cout << "\nPointer to the holder was found to be NULL." << endl;
        }
        elements = obj.elements;
        size = obj.size;
    }

    t getData(int pos) {
        if (pos < 0 || pos > size)
        {
            cout << "\nError: Invalid index!" << endl;
            return 0;
        }

        return ptr[pos];
    }

    int sizeA() {
        return size;
    }

    int capacity() {
        int capacity = size - elements;
        return capacity;
    }

    void insert(t data) {
        int pos;

        if (elements >= size) {
            int updatedSize = (2 * size);

            t* tempPtr = ptr;
            ptr = new t[updatedSize];

            for (int i = 0; i < size; ++i) {
                ptr[i] = tempPtr[i];
            }

            delete[] tempPtr;
            tempPtr = NULL;

            size = updatedSize;
            pos = elements;
            ptr[pos] = data;
            incrementElement();
        }
        else {
            if (elements == 0) {
                pos = 0;
                ptr[pos] = data;
                incrementElement();
            }
            else {
                pos = elements;
                ptr[pos] = data;
                incrementElement();
            }
        }
    }

    bool compare(const dynamicDataHolder<t>& obj)
    {
        bool result = false;

        if (obj.size != size || obj.elements != elements) {
            return result;
        }

        for (int i = 0; i < size; i++) {
            result = false;

            if (ptr[i] == obj.ptr[i])
            {
                result = true;
            }
        }

        return result;
    }

    void resize(int updatedSize) {
        t* tempPtr = ptr;
        ptr = new t[updatedSize];

        if (updatedSize < size) {
            cout << "\nWarning! Entered size is less than the current size." << endl
                << "Loss of data may occur. Continue? (Y/N)" << endl;
        Select:
            char userChoice; cin >> userChoice;
            
            if (userChoice == 'y' || userChoice == 'Y') {
                goto Continue;
            }
            else if (userChoice == 'n' || userChoice == 'N') {
                return;
            }
            else {
                cout << "\nInvalid selection - please try again." << endl;
                goto Select;
            }
        }
        Continue:
        if (ptr != NULL) {
            for (int i = 0; i < size; i++) {
                ptr[i] = tempPtr[i];
            }

            delete[] tempPtr;
            tempPtr = NULL;

            size = updatedSize;
        }
        else {
            cout << "\nPointer to the holder was found to be NULL." << endl;
        }
    }

    void insertAtNthPos()
    {
        cout << "\nEnter the details below to insert values:-\n" << endl;
        cout << "\nPosition: ";
        int pos = 0; cin >> pos;
        cout << "Value: ";
        t value; cin >> value;

        if (pos <= size)
        {
            ptr[pos] = value;

            if (pos > elements) {
                incrementElement();
            }
        }
        else {
            resize(2 * size);
            ptr[pos] = value;
            incrementElement();
        }
    }

    dynamicDataHolder& operator=(const dynamicDataHolder& rhs) {
        delete[] ptr;
        ptr = new t[rhs.size];
        if (this != &rhs)
        {
            for (int i = 0; i < rhs.size; i++)
            {
                ptr[i] = rhs.ptr[i];
            }
        }
        return *this;
    }

    ~dynamicDataHolder()
    {
        delete[] ptr;
        ptr = NULL;
    }

    void incrementElement()
    {
        elements += 1;
    }

};


int main()
{
    cout << "What do you want to do?" << endl
        << "\t1. Run Test Sequence" << endl 
        << "\t0. Exit Program" << endl;
Select:
    int userChoice = 0; cin >> userChoice;

    if (userChoice == 1) {
        goto testSequence;
    }
    else if (userChoice == 0) {
        return 0;
    }
    else {
        cout << "\nInvalid selection - please try again." << endl;
        goto Select;
    }


testSequence:
    system("CLS");

    dynamicDataHolder<int> obj;
    dynamicDataHolder<char> obj2(5);
    dynamicDataHolder<char> obj3 = obj2;

    bool result = (obj3.compare(obj2));
    if (result == 1) {
        cout << "True" << endl;
    }
    else {
        cout << "\nFalse" << endl;
    }

    for (int i = 0; i < 5; i++) {
        obj.insert(i * 2);
    }

    cout << "Capacity: " << obj.capacity() << endl;
    cout << "Size: " << obj.sizeA() << endl;

    obj.resize(15);

    cout << obj2.getData(4) << endl;

    obj2.insertAtNthPos();

    dynamicDataHolder<int> obj4;
    obj4 = obj;
}
#包括
使用名称空间std;
模板
类别持有人
{
t*ptr=NULL;
int size=0;
int元素=0;
公众:
动态持有者(){
ptr=新t[5];
尺寸=5;
元素=0;
}
dynamicDataHolder(整数数组化){
ptr=新的t[尺寸];
大小=阵列化;
元素=0;
}
dynamicDataHolder(常量dynamicDataHolder和obj){
ptr=新t【目标尺寸】;
如果(ptr!=NULL){
对于(int i=0;i
这是您的代码的简化版本

  • 用循环取代
    goto
    (语言开发的这场革命发生在1970年,与时俱进!)

  • 使用正确的名称

    特别是

    • 容量意味着“限制潜在大小”(您之前称之为“大小”或“阵列化”)
    • 大小表示“集合中元素的计数”(例如,插入时大小会增加)
    • (容量-大小)是可用空间/房间/可用容量。这是您之前命名的“容量”
    违反对标识符含义的期望会导致错误。事实上,您有错误,因为您自己的测试序列在包含0个元素(
    \u size==0
    )的保持架上使用了
    getData(4)

  • 操作也一样。
    insert(…)
    应该插入,而不是覆盖(见下文),
    resize(…)
    应该更改
    大小,而不是
    容量

    为了改变容量,标准库使用术语
    reserve(…)
    ,我也使用它来保持一致性和最少的惊喜

  • 此外,名称应该是描述性的。在强类型语言中,以其类型命名变量是冗余信息。此外,您可以命名变量
    strA
    strB
    strC
    ,但命名
    givenName
    middleNames
    姓氏>有语义信息(是
    姓氏
    姓氏
    出生名
    家庭名
    。传达了这么多信息)

    从这个意义上讲,我将
    ptr
    重命名为
    \u data

    当我们使用时,我们可能会将该类重命名为描述性的:
    dynaray
    告诉您的不仅仅是
    dynamicDataHolder
    (可以是
    std::optional
    std::unique\u ptr
    std::vector
    harddiskVolume
    等)

    作为一个次要的脚注,最好为(私有)成员变量制定一个命名约定,这样您就可以知道哪些变量是本地变量,哪些是成员变量

  • 你有很多地方(如果我没记错的话,有两个)进行了一次边界检查(想想
    pos>容量
    ,而不是
    =

  • 关注点分离。
    dynamicDataHolder
    是一个数据持有者,而不是一个关于条件的
    InteractiveUserInterviewAboutorConditions
    databaseUpdateUserInterface
    。因此,不要在类中执行UI。将
    void insertAtNthPos()
    更改为
    void setData(size\t pos,t value)

    再次强调,命名是重要的。如果你真的不同意,不要承诺插入,除非它超出了当前的大小限制。“在{1,2,3}的位置1插入7”应该导致“{1,7,2,3}”,而不是“{1,7,3}”。名称
    setData

    • 做预期的事
    • 分离用户界面关注点
    • 漂亮地镜像
      getData(大小\u t位置)
  • 哦,使用一致的索引类型(在我的版本中为size\t),这样可以避免由于混合有符号/无符号比较而导致的滑稽检查(
    pos>0
    ?)和错误

    是的,你有它们。不,你不会抓到它们,因为你没有启用编译器警告。让这强化咒语:“使用强制诊断”

  • 不要在全局范围内“使用命名空间”。或者根本不要()

  • DRY.DRY.DRY.(不要重复你自己)。这样的例子太多了。你至少有3个复制元素数据循环。你有几个地方可以调整分配的数组的大小。你有一些有趣的东西,比如:

     void insert(t data) {
         int pos;
    
         if (elements >= size) {
             int updatedSize = (2 * size);
    
             t* tempPtr = ptr;
             ptr = new t[updatedSize];
    
             for (int i = 0; i < size; ++i) {
                 ptr[i] = tempPtr[i];
             }
    
             delete[] tempPtr;
             tempPtr = NULL;
    
             size = updatedSize;
             pos = elements;
             ptr[pos] = data;
             incrementElement();
         }
         else {
             if (elements == 0) {
                 pos = 0;
                 ptr[pos] = data;
                 incrementElement();
             }
             else {
                 pos = elements;
                 ptr[pos] = data;
                 incrementElement();
             }
         }
    
    哦,哎呀,这完全被破坏了,因为实际上,插入没有什么特别的。但是
    setData
    也只有~4行代码

  • 当你添加物品时,你必须小心:你进行了一次非常有用的“容量检查”。如果容量不足,你会将容量加倍。然而,谁说这足够了?你需要继续加倍,直到空间足够:

     void setData(size_t pos, T value) {
         while (pos >= _capacity) {
             grow();
         }
         _size      = std::max(_size, pos + 1);
         _data[pos] = value;
     }
    
    或立即保留足够的尺寸:

     size_t required = _capacity;
     while (pos >= required)
         required *= 2;
     reserve(required);
    
  • 常量正确性:所有观察者都应标记为
    Const
    ,以便编译器知道它可以优化什么,并且用户保证什么永远不会改变:

     size_t size()      const { return _size;     }
     size_t capacity()  const { return _capacity; }
     size_t available() const { return _capacity  - _size; }
    
     bool compare(const DynArray<T>& obj) const;
    
    现在,复制构造函数可以是:

     DynArray(DynArray const& obj) : DynArray(obj.capacity()) {
         _size = obj.size();
         for (size_t i = 0; i < capacity(); ++i)
             _data[i] = obj._data[i];
     }
    
    现在真正的魔力在于移动构造函数/赋值操作

  • 将习惯用法复制和交换到rescue中。事实证明,您可以非常有效地实现这些操作,而且通常只需要很少的代码:

     DynArray(DynArray&& obj) : DynArray(0) { swap(obj); }
     DynArray& operator=(DynArray rhs) {
         swap(rhs);
         return *this;
     }
    
    请参阅。简单性是一个驱动程序。关键是您只需使用临时性进行交换。临时性将根据您的需要进行破坏。交换本身非常简单:

     void swap(DynArray& rhs) {
         assert(rhs._data);
         std::swap(_capacity, rhs._capacity);
         std::swap(_size, rhs._size);
         std::swap(_data, rhs._data);
     }
    
    复制和交换的背后还有很多想法,但如果您真的愿意,您可以自己找到:

  • 比较检查电容
     void reserve(size_t updatedSize) {
         DynArray tmp(updatedSize);
         for (size_t i = 0; i < std::min(capacity(), tmp.capacity()); ++i) {
             tmp._data[i] = _data[i];
         }
         *this = std::move(tmp);
     }
    
     void grow() { reserve(2 * _capacity); }
    
     DynArray(DynArray&& obj) : DynArray(0) { swap(obj); }
     DynArray& operator=(DynArray rhs) {
         swap(rhs);
         return *this;
     }
    
     void swap(DynArray& rhs) {
         assert(rhs._data);
         std::swap(_capacity, rhs._capacity);
         std::swap(_size, rhs._size);
         std::swap(_data, rhs._data);
     }
    
      DynArray<int> a(20), b(12);
      for (int v : {1,2,3}) { a.insert(v); b.insert(v); }
      assert(a == b); // should pass
    
     result = false;
    
     if (ptr[i] == obj.ptr[i])
     {
         result = true;
     }
    
    int main() {
        std::cout << "What do you want to do?\n"
                  << "\t1. Run Test Sequence\n"
                  << "\t0. Exit Program\n";
    
        for (int userChoice = 0; std::cin >> userChoice;) {
            switch (userChoice) {
                case 1: testSequence(); break;
                case 0: return 0;
                default: std::cout << "\nInvalid selection - please try again.\n";
            }
        }
    }
    
    #include <iostream>
    #include <stdexcept>
    #include <cassert>
    
    template <class T> class DynArray {
        size_t _capacity = 0;
        size_t _size     = 0;
        T* _data         = nullptr;
    
      public:
        explicit DynArray(size_t initialCapacity = 5)
            : _capacity(initialCapacity),
              _data(initialCapacity != 0u ? new T[_capacity]{} : nullptr) {}
    
        DynArray(DynArray const& obj) : DynArray(obj.capacity()) {
            _size = obj.size();
            for (size_t i = 0; i < capacity(); ++i)
                _data[i] = obj._data[i];
        }
    
        ~DynArray() { delete[] _data; }
    
        void swap(DynArray& rhs) {
            assert(rhs._data);
            std::swap(_capacity, rhs._capacity);
            std::swap(_size, rhs._size);
            std::swap(_data, rhs._data);
        }
    
        DynArray(DynArray&& obj) : DynArray(0) { swap(obj); }
        DynArray& operator=(DynArray rhs) {
            swap(rhs);
            return *this;
        }
    
        T getData(size_t pos) const {
            if (pos >= _size) {
                throw std::out_of_range("pos");
            }
    
            return _data[pos];
        }
    
        void setData(size_t pos, T value) {
            while (pos >= _capacity) 
                grow();
            _size = std::max(_size, pos+1);
            _data[pos] = value;
        }
    
        void insert(T data) { setData(_size, data); }
        size_t size()      const { return _size;     }
        size_t capacity()  const { return _capacity; }
        size_t available() const { return _capacity  - _size; }
    
        bool compare(const DynArray& obj) const {
            if (obj._size != _size) {
                return false;
            }
    
            for (size_t i = 0; i < _size; i++) {
                if (_data[i] != obj._data[i])
                    return false;
            }
    
            return true;
        }
    
        void reserve(size_t updatedSize) {
            DynArray tmp(updatedSize);
            for (size_t i = 0; i < std::min(capacity(), tmp.capacity()); ++i) {
                tmp._data[i] = _data[i];
            }
            *this = std::move(tmp);
        }
    
        void grow() { reserve(2 * _capacity); }
    };
    
    template <typename T>
    void dump(std::string caption, DynArray<T> const& da) {
        std::cout << caption << " Capacity: "  << da.capacity()  << ","
                             << " Size: "      << da.size()      << ","
                             << " Available: " << da.available() << ","
                             << " Data: {";
        for (size_t i = 0; i < da.size(); i++)
            std::cout << " " << static_cast<int>(da.getData(i));
        std::cout << " }\n";
    }
    
    void testSequence() {
        {
            DynArray<int> obj;
            for (size_t i = 0; i < 5; i++) {
                obj.insert(i * 2);
            }
    
            dump("obj", obj);
    
            if (15 < obj.capacity()) {
                std::cout << "\nWarning! Entered size is less than the current size.\n"
                    << "Loss of data may occur. Continue? (Y/N)\n";
    
                for (char userChoice; std::cin >> userChoice;) {
                    if (userChoice == 'y' || userChoice == 'Y') {
                        obj.reserve(15);
                        break;
                    }
                    if (userChoice == 'n' || userChoice == 'N') {
                        break;
                    }
                    std::cout << "\nInvalid selection - please try again.\n";
                }
            }
    
            DynArray<int> obj4;
            obj4 = obj;
    
            dump("obj4", obj);
        }
    
        DynArray<char> obj2(5);
        DynArray<char> obj3 = obj2;
    
        dump("obj2", obj2);
        dump("obj3", obj3);
        std::cout << "obj2 == obj3: " << std::boolalpha << obj3.compare(obj2) << "\n";
    
        try {
            std::cout << "obj2.getData(4): " << obj2.getData(4) << std::endl;
        } catch(std::out_of_range const& e) {
            std::cout << "out of range: " << e.what() << std::endl;
        }
    
        while (true) {
            std::cout << "\nEnter the details below to insert obj2 values:-\n";
            std::cout << "\nPosition: ";
            long pos = 0;
            if (std::cin >> pos) {
                if (pos < 0)
                    break;
                std::cout << "Value: ";
                int value = 0;
                if (std::cin >> value) {
                    obj2.setData(pos, value);
                }
            }
            if (!std::cin.eof())
                std::cin.clear();
            else
                break;
            
            dump("obj2", obj2);
        }
    }
    
    int main() {
        do {
            std::cout << "What do you want to do?\n"
                      << "\t1. Run Test Sequence\n"
                      << "\t0. Exit Program\n";
            if (int userChoice = 0; std::cin >> userChoice) {
                switch (userChoice) {
                    case 1: testSequence(); continue;
                    case 0: std::cout << "Goodbye\n"; return 0;
                    default: std::cout << "\nInvalid selection - please try again.\n";
                }
            }
            if (!std::cin.eof())
                std::cin.clear();
        } while (std::cin.good());
    }
    
    g++ -std=c++17 -O2 -Wall -Wextra -pedantic -pthread -fsanitize=address,undefined main.cpp -o sotest
    ./sotest <<< "1 30 99 3 42 0 -1 0 8 -1 1 -1 0"