1. 程式人生 > >C++面試常見題目6_STL中vector

C++面試常見題目6_STL中vector

Vector概述

vector是同一種類型的物件的集合,每個物件都有一個對應的整數索引值。屬於序列容器(vector,list,deque)的一種。

vector是一種允許快速隨機訪問其中元素的線性序列。關鍵在於其對大小的控制以及重新配置時的資料移動速率。

Vector空間分配策略

vector初始化會分配比客戶端需求量更大一些的空間,以便將來可能的擴充。

擴充空間需要經過的步驟:重新分配更大的空間,使用拷貝建構函式進行元素移動,呼叫解構函式銷燬舊物件,最後釋放舊記憶體空間。

具體過程:當新增一個元素時,如果容器已經沒有空間容納新的元素,此時,由於元素必須連續儲存以便索引訪問,所以不能在記憶體中隨機找個地方儲存這個新元素。於是vector必須重新分配儲存空間,用來存放原來的元素以及新新增的元素,存放在舊儲存空間的元素被複制到新儲存空間中,接著插入新元素,最後撤銷舊的儲存空間。每當vector容器不得不分配新的儲存空間時,以加倍當前容量的分配策略實現重新分配。

注意:一旦vector空間重新配置,則指向原來vector的所有迭代器都失效了,因為vector的地址改變了。

Vector的資料結構

Vector的定義摘要

#include<iostream>
using namespace std;
#include<memory.h>  

// alloc是SGI STL的空間配置器
template <class T, class Alloc = alloc>
class vector
{
public:
    // vector的巢狀型別定義,typedefs用於提供iterator_traits<I>支援
    typedef T value_type;
    typedef value_type* pointer;
    typedef value_type* iterator;
    typedef value_type& reference;
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;

protected:
    // 這個提供STL標準的allocator介面
    typedef simple_alloc <value_type, Alloc> data_allocator;

    iterator start;               // 表示目前使用空間的頭
    iterator finish;              // 表示目前使用空間的尾
    iterator end_of_storage;      // 表示實際分配記憶體空間的尾

    void insert_aux(iterator position, const T& x);

    // 釋放分配的記憶體空間
    void deallocate()
    {
        if (start)
            data_allocator::deallocate(start, end_of_storage - start);
    }

    //填充並予以初始化
    void fill_initialize(size_type n, const T& value)
    {
        start = allocate_and_fill(n, value);
        finish = start + n;              
        end_of_storage = finish;
    }

public:
    iterator begin() { return start; }
    iterator end() { return finish; }

    size_type size() const { return size_type(end() - begin()); }
    size_type max_size() const { return size_type(-1) / sizeof(T); }
    size_type capacity() const { return size_type(end_of_storage - begin()); }
    bool empty() const { return begin() == end(); }
    reference operator[](size_type n) { return *(begin() + n); }

    //預設建構函式不分配記憶體空間
    vector() : start(0), finish(0), end_of_storage(0) {}

    vector(size_type n, const T& value) { fill_initialize(n, value); }
    vector(int n, const T& value) { fill_initialize(n, value); }
    vector(long n, const T& value) { fill_initialize(n, value); }
    
    //需顯示呼叫的建構函式
    explicit vector(size_type n) { fill_initialize(n, T()); }

    ~vector()
    {
        destroy(start, finish);
        deallocate();
    }

    // 提供訪問函式
    reference front() { return *begin(); }
    reference back() { return *(end() - 1); }

  
    void push_back(const T& x)
    {
        // 記憶體滿足條件則直接追加元素, 否則需要重新分配記憶體空間
        if (finish != end_of_storage)
        {
            construct(finish, x);
            ++finish;
        }
        else
            insert_aux(end(), x);
    }


    iterator insert(iterator position, const T& x)
    {
        size_type n = position - begin();
        if (finish != end_of_storage && position == end())
        {
            construct(finish, x);
            ++finish;
        }
        else
            insert_aux(position, x);
        return begin() + n;
    }

    iterator insert(iterator position) { return insert(position, T()); }

    void pop_back()
    {
        --finish;
        destroy(finish);
    }

    iterator erase(iterator position)
    {
        if (position + 1 != end())
            //後續元素向前移
            copy(position + 1, finish, position);
        --finish;
        destroy(finish);
        return position;
    }


    iterator erase(iterator first, iterator last)
    {
        iterator i = copy(last, finish, first);
        destroy(i, finish);
        finish = finish - (last - first);
        return first;
    }

    // 調整size, 但是並不會重新分配記憶體空間
    void resize(size_type new_size, const T& x)
    {
        if (new_size < size())
            erase(begin() + new_size, end());
        else
            insert(end(), new_size - size(), x);
    }
    void resize(size_type new_size) { resize(new_size, T()); }

    void clear() { erase(begin(), end()); }

protected:
    // 配置空間並填滿內容
    iterator allocate_and_fill(size_type n, const T& x)
    {
        iterator result = data_allocator::allocate(n);
        uninitialized_fill_n(result, n, x);
        return result;
    }
};

Vector的構造與記憶體管理

    void push_back(const T& x)
    {
        // 記憶體滿足條件則直接追加元素, 否則需要重新分配記憶體空間
        if (finish != end_of_storage)
        {
            construct(finish, x);
            ++finish;
        }
        else
            insert_aux(end(), x);
    }

    //空間不足,重新分配空間的具體操作
    template <class T, class Alloc>
    void insert_aux(iterator position, const T& x)
    {
        if (finish != end_of_storage)    // 還有備用空間
        {
            // 在備用空間起始處構造一個元素,並以vector最後一個元素值為其初值
            construct(finish, *(finish - 1));
            ++finish;
            T x_copy = x;
            copy_backward(position, finish - 2, finish - 1);
            *position = x_copy;
        }
        else   // 已無備用空間
        {
            const size_type old_size = size();
            const size_type len = old_size != 0 ? 2 * old_size : 1;
            // 以上配置元素:如果大小為0,則配置1(個元素大小)
            // 如果大小不為0,則配置原來大小的兩倍
            // 前半段用來放置原資料,後半段準備用來放置新資料

            iterator new_start = data_allocator::allocate(len);  // 實際配置
            iterator new_finish = new_start;
            // 將記憶體重新配置
            try
            {
                // 將原vector的安插點以前的內容拷貝到新vector
                new_finish = uninitialized_copy(start, position, new_start);
                // 為新元素設定初值 x
                construct(new_finish, x);
                // 調整水位
                ++new_finish;
                // 將安插點以後的原內容也拷貝過來
                new_finish = uninitialized_copy(position, finish, new_finish);
            }
            catch(...)
            {
                // 回滾操作
                destroy(new_start, new_finish);
                data_allocator::deallocate(new_start, len);
                throw;
            }
            // 析構並釋放原vector
            destroy(begin(), end());
            deallocate();

            // 調整迭代器,指向新vector
            start = new_start;
            finish = new_finish;
            end_of_storage = new_start + len;
        }
    }

Vector的元素操作:程式碼為insert

    template <class T, class Alloc>
    void insert(iterator position, size_type n, const T& x)
    {
        // 如果n為0則不進行任何操作
        if (n != 0)
        {
            if (size_type(end_of_storage - finish) >= n)
            {      
                // 剩下的備用空間大於等於“新增元素的個數”
                T x_copy = x;
                // 以下計算插入點之後的現有元素個數
                const size_type elems_after = finish - position;
                iterator old_finish = finish;
                if (elems_after > n)
                {
                    // 插入點之後的現有元素個數 大於 新增元素個數
                    uninitialized_copy(finish - n, finish, finish);
                    finish += n;    // 將vector 尾端標記後移
                    copy_backward(position, old_finish - n, old_finish);
                    fill(position, position + n, x_copy); // 從插入點開始填入新值
                }
                else
                {
                    // 插入點之後的現有元素個數 小於等於 新增元素個數
                    uninitialized_fill_n(finish, n - elems_after, x_copy);
                    finish += n - elems_after;
                    uninitialized_copy(position, old_finish, finish);
                    finish += elems_after;
                    fill(position, old_finish, x_copy);
                }
            }
            else
            {   // 剩下的備用空間小於“新增元素個數”(那就必須配置額外的記憶體)
                // 首先決定新長度:就長度的兩倍 , 或舊長度+新增元素個數
                const size_type old_size = size();
                const size_type len = old_size + max(old_size, n);
                // 以下配置新的vector空間
                iterator new_start = data_allocator::allocate(len);
                iterator new_finish = new_start;
                __STL_TRY
                {
                    // 以下首先將舊的vector的插入點之前的元素複製到新空間
                    new_finish = uninitialized_copy(start, position, new_start);
                    // 以下再將新增元素(初值皆為n)填入新空間
                    new_finish = uninitialized_fill_n(new_finish, n, x);
                    // 以下再將舊vector的插入點之後的元素複製到新空間
                    new_finish = uninitialized_copy(position, finish, new_finish);
                }
#         ifdef  __STL_USE_EXCEPTIONS
                catch(...)
                {
                    destroy(new_start, new_finish);
                    data_allocator::deallocate(new_start, len);
                    throw;
                }
#         endif /* __STL_USE_EXCEPTIONS */
                destroy(start, finish);
                deallocate();
                start = new_start;
                finish = new_finish;
                end_of_storage = new_start + len;
            }
        }
    }

本系列文章目的為個人準備面試的簡單總結,文中多有不足,敬請批評指正!

參考:

相關推薦

C++面試常見題目6_STLvector

Vector概述 vector是同一種類型的物件的集合,每個物件都有一個對應的整數索引值。屬於序列容器(vector,list,deque)的一種。 vector是一種允許快速隨機訪問其中元素的線性序列。關鍵在於其對大小的控制以及重新配置時的資料移動速率。 Vector

C++面試常見題目問與答(彙總二)

上一次已經謝了一些東西了,感覺總結的差不多了,這一期主要是上一期的查漏補缺。主要是側重回答一些比較重一些的問題,比如智慧指標、RAII機制還有最長被問到的C++的多型。 首先是智慧指標。 1.智慧指標 上一期介紹一下智慧指標在用法的上要注意的部分,這次主要

C++面試常見題目4_記憶體管理,記憶體洩露

記憶體管理 定義:記憶體管理是指軟體執行時對計算機記憶體資源的分配和使用的技術。其最主要的目的是如何高效,快速的分配,並且在適當的時候釋放和回收記憶體資源。 在C++中記憶體分為5個區,分別是堆、棧、自由儲存區、全域性/靜態儲存區和常量儲存區。 堆:堆是

C++面試常見題目5_面向物件的三大特性(封裝,繼承,多型)

面向物件的三大特性 封裝 定義:將資料和對該資料進行合法操作的函式封裝在一起作為一個類的定義,即用類進行資料抽象。 繼承 定義:用類派生從一個類繼承另一個類,派生類繼承基類的成員。 訪問控制與繼承 訪問方式

C++面試常見題目1_C++與C,Java的區別

new/delete是C++的運算子,malloc/free是C/C++語言的標準庫函式。 new無需顯式地指出所需記憶體的尺寸,編譯器會根據型別資訊自行計算。而malloc則需要顯式地指出所需記憶體的尺寸。 new操作符記憶體分配成功時,返回的是物件型別的指標,型別嚴格與物件匹配,無須進行型別轉換。而m

C++程式設計師面試常見題目

歡迎大家指正問題  1:請用簡單的語言告訴我C++ 是什麼? 答:C++是在C語言的基礎上開發的一種面向物件程式語言,應用廣泛。C++支援多種程式設計正規化 --面向物件程式設計、泛型程式設計和過程化程式設計。 其程式設計領域眾廣,常用於系統開發,引擎開發等應

mysql面試常見題目

arc delete upd replace eva 第三題 其中 signed 使用 第一題 某班學生和考試成績信息如下表Student所示: Student表 ID SName Mark 1 Jack 90 2 Marry 96 3 Rose 88 4 Bob 86 5

mysql面試常見題目2

4條 count student 插入 機考 mysq ID def arch Sutdent表的定義 字段名 字段描述 數據類型 主鍵 外鍵 非空 唯一 自增 Id 學號 INT(10) 是 否 是 是 是 sName 姓名 VARCHAR(20) 否 否 是 否 否 S

C++面試常見問題

還需要 虛繼承 基本類型 釋放 希望 全局區 類別 重用 引用 轉載:https://zhuanlan.zhihu.com/p/34016871?utm_source=qq&utm_medium=social 1.在C++ 程序中調用被C 編譯器編譯後的函數,為

C++面試 常見手撕程式碼

1、氣泡排序 #include <iostream> using namespace std; void swap(int &a, int &b){ int temp = a; a = b; b = temp; } void

C++ 標準模板庫STLvector用法介紹

本文所介紹的std::vector用法基於C++11,std::vector定義於標頭檔案<vector>中,其定義如下:template< class T, class Allocator = std::allocator<T>

面試常見題目:大數相加的Java實現(考慮負數情況)

前言: 在做面試題目時,我們經常看到有這樣的題目:將兩個很大的數相加,超過100位。 網上可以看到很多答案,但是這些答案大部分都是沒有考慮負數的情況。 首先我們已經不能直接用long型別進行表示了

C++面試常見的幾個庫函式詳解

strcpy() 原型宣告:char strcpy(char dest, const char *src);  功能:把從src地址開始且含有NULL結束符的字串複製到以dest開始的地址空間  說明:src和dest所指記憶體區域不可以重疊且dest必須有足夠的空間來容

面試常見題---------java抽象類跟介面的區別

    含有abstract修飾符的class 即為抽象類,abstract類不能建立例項物件,含有abstract的方法的類必須定義為abstract class ,abstract class 裡

c++面試常見問題總結

近來在面試的過程,發現面試官在c++方面總是喜歡問及的一些相關問題總結,當時沒怎麼答出來,或者是答的不怎麼全面,故而查詢相關資料總結下。(後面實際工作會進行實時更新資訊) <一>c++虛擬函式方面     虛擬函式(Virtual Function)是通過一張

Linux 筆試面試常見題目(整理)

一.填空題: 在Linux系統中,以 檔案 方式訪問裝置 。 Linux核心引導時,從檔案 /etc/fstab 中讀取要載入的檔案系統。 Linux檔案系統中每個檔案用 i節點 來標識。 全部磁碟塊由四個部分組成,分別為引導塊 、專用塊 、 i節點表塊 和

java面試/筆試題目之Java常見集合(持續更新)

宣告:題目大部分來源於Java後端公眾號,有些個人整理,但答案皆為個人整理,僅供參考。 目錄 Java中的集合 List 和 Set 區別 1.Set:集合中的物件不按特定方式排序(針對記憶體地址來說,即非線性),並且沒有重複物件。它的有些實現類能對集合中的物件按特定方式排序。

一個程式包含C++ STLvector常見用法

廢話少說,直接上程式碼:  #include <iostream> #include <vector> using namespace std; int main() { //vector的四種構造方式 //構造一個空vector vect

C#.NET常見問題(FAQ)-VS如何整個項目查找字符串

c# csr 電機 you 對話框 ges .net 所有 空間 Ctrl+F打開查找對話框,然後輸入查找字符串,電機右邊的小三角,選擇整個解決方案,就可以遍歷所有文件查找指定字符了 ? ?更多教學視頻和資料下載,歡迎關註以下信息: 我的優酷空間: ht

C#.NET常見問題(FAQ)-如何在系統變量加入新的環境變量

教學視頻 分號 新的 alt 文件 sof 問題 png 點擊 比如我要將C:\Windows\Microsoft.NET\Framework\v3.5這個目錄加入環境變量 則在系統的環境變量中點擊Path,編輯,然後加入一個分號";",然後粘貼新的地址。