簡體   English   中英

std :: vector如何破壞它的對象?

[英]How does std::vector destruct its objects?

為了練習,我正在嘗試實現自己的std :: vector。 當前源代碼: http//pastebin.com/bE4kjzcb


這是我班級的概要:

  1. Array()使用malloc()分配一些內存
  2. push_back(const T &t)添加一個元素,必要時調用realloc()
  3. ~Array()調用free()來釋放內存。

這個模型的主要問題是free()循環內存,但它不調用T的析構函數(當T是類而不是標准數據類型時)。

當向量內的元素是對象時,這可能導致嚴重的資源泄漏。 我解決這個問題的方法是,在free()內存之前調用~T() EXPLICITLY

我使用malloc()的原因是,我正在嘗試使用realloc() 如果我使用newdelete ,則重新分配時內存使用率將達到峰值。 (新緩沖區和舊緩沖區都存在的時刻。)

問:這是一個糟糕的設計嗎? std::vector如何解決這個問題? 我的矢量類中還有其他缺陷嗎?

PS:現在不要談論malloc()多線程性能。

調用~T() 正是 std::vector處理問題的方式。

但是你有幾個問題:

首先, push_back需要使用placement new來將值復制 - 構造到向量中。 你不能只使用作業。

其次,你不能調用realloc - 如果對象有內部指針,它們最終會指向自己的外部。 您必須再次調用malloc ,然后使用placement new復制構造值,然后顯式刪除所有舊值,然后調用free釋放舊值。

(實際上, std::vector不會調用~T()本身。而是調用分配器來負責... 分配和釋放內存。但在內部,這就是通用分配器的用法。)

push_back(const T&t)添加一個元素,必要時調用realloc()。

只要T可以trivially copiable ,就可以了,例如,嘗試推回雙鏈表,並且在重新完成后需要一個並向后迭代 - 應用程序可能會崩潰。 解決方案是將函數重載兩次,一個用於可瑣事可復制的類型,另一個用於非瑣事的對象。

與其他人相反,我很遺憾標准容器不會將realloc用於可以輕易處理的對象。 至少在Windows上, realloc首先檢查當前塊是否可以保持新的大小,如果是 - 它只是更新堆條目,從而導致巨大的性能提升(無需復制)。

在我釋放()內存之前調用~T()顯式。

是的,這就是標准分配器的工作方式。 假設size是對象計數,您迭代每個並手動銷毀它:

for (auto i=0U;i<size;i++){
   data[i].~T();
}

有趣的是,C ++ 17將添加std::destruct ,它就是這樣做的。

獎勵:使用new[]delete[]在這里沒有幫助。 通常,動態數組比實現容量所需的空間節省更多空間 ,額外的空間不是用活動對象填充,只是垃圾。

new[]將用對象填充整個內存。 容量無法以這種方式實現。 每當有人推回新元素時,數組將移動/復制整個對象。 所以在1000次push_back之后,將有1000次重新分配。 我們希望攤銷時間為O(log (n))

甚至標准的分配器也會調用new(size_t)malloc而不是new[]

而不是調用mallocfree ,更喜歡使用newdelete 調用delete將確保調用實例dtor。 =)

如果默認構造函數僅為已分配的對象保留,並且T的/ move / copy構造函數/賦值運算符和析構函數傳播剛分配的信息或用戶對象 ,則可以使用new [] std :: vector中的解決方案及其默認分配器是一個更好的設計。

那個工程

buffer = new T[capacity];

代替

buffer = (T*)malloc(capacity * sizeof(T));

delete [] buffer;

代替

free(buffer);

將自動調用每個對象的析構函數,如示例所示

class A {
public:
  ~A() { std::cout << "ok" << std::endl; }
};

int main() {
  A* a = new A[3];
  delete [] a;
  return 0;
}

此代碼輸出3“ok”。 然后,A應包含其他字段和非默認構造函數,以區分分配和用戶構造。

這里有一個例子,它是如何工作的或多或少std :: vector:

#ifndef __STDVECTOR__
#define __STDVECTOR__

#include <iostream>

using namespace std;

template <typename T>
    class StdVector{
        private:
            T *buffer;
            unsigned int capacity;
        public:
            //Constructor.
            StdVector(){
                capacity=0;
                buffer=new T[capacity];
            }
            //Copy constructor.
            StdVector(const StdVector &asv){
                int i;

                capacity=asv.getCapacity();
                buffer=new T[asv.getCapacity()];
                for (i=0; i<capacity; i++){
                    buffer[i]=asv[i];
                }
            }
            //Destructor.
            ~StdVector(){
                delete []buffer;
            }
            void push_back(T obj){
                StdVector oldSV(*this);
                int i;

                capacity++;
                delete []buffer;
                buffer=new T[capacity];
                for (i=0; i<oldSV.getCapacity(); i++){
                    buffer[i]=oldSV[i];
                }
                buffer[i]=obj;
            };
            T getBuffer() const{
                if (capacity==0){
                    throw exception();
                }
                return *buffer;
            };
            T &operator[](int index) const{
                if (index>=capacity){
                    //Out of range.
                    throw exception();
                }
                else{
                    return buffer[index];
                }
            }
            StdVector &operator=(const StdVector &obj){
                capacity=obj.getCapacity();
                delete []buffer;
                buffer=new T[capacity];
                buffer=obj.getBuffer();
                return *this;
            }
            unsigned int getCapacity() const{
                return capacity;
            };
    };

#endif

int main(){
    try{
        StdVector<int> test;
        StdVector<string> test2;
        unsigned int i;

        test.push_back(5);
        test.push_back(4);
        test.push_back(3);
        test.push_back(2);
        test.push_back(1);
        test.push_back(0);
        test.push_back(-1);
        test.push_back(-2);
        test.push_back(-3);
        test.push_back(-4);
        test.push_back(-5);
        for (i=0; i<test.getCapacity(); i++){
            cout << test[i] << endl;
        }
        test2.push_back("Hello");
        test2.push_back(" ");
        test2.push_back("World");
        test2.push_back(".");
        cout << "---------------" << endl;
        for (i=0; i<test2.getCapacity(); i++){
            cout << test2[i];
        }
        cout << endl;
    }
    catch(...){
        cout << "Exception." << endl;
    }
    return 0;
}

它打印:

4

3

2

1

0

-1

-2

-3

-4

-5

---------------

你好,世界。

也許我有一些錯誤。 如果你知道的話,請跟我說。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM