2011-12-15 27 views
1

Accelerated C++的第11章介紹了實現模板類的過程,以STL的向量類的簡化版本爲例。練習11-6希望我們將.erase().clear()方法添加到類中,所以我首先直接從書中複製最終代碼並嘗試編譯,但失敗了。然後我把所有的函數定義移動到.h文件中(根據需要刪除Vec<T>::等東西)並編譯我的main.cpp,它工作。實現簡化的C++向量類副本 - 不會編譯

這裏是我的所有代碼:

的main.cpp

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

using std::cout; 
using std::endl; 

int main() 
{ 
    Vec<int> v; 
    for (int i = 1; i < 10; ++i) 
     v.push_back(i); 

    for(Vec<int>::const_iterator iter = v.begin(); 
     iter != v.end(); ++iter) 
     cout << *iter << endl; 

    return 0; 
} 

Vec.h

#ifndef GUARD_Vec_h 
#define GUARD_Vec_h 

#include <cstddef> 
#include <memory> 

template <class T> class Vec { 
public: 
    // member variables 
    typedef T* iterator; 
    typedef const T* const_iterator; 
    typedef std::size_t size_type; 
    typedef T value_type; 
    typedef T& reference; 
    typedef const T& const_reference; 

    // constructors + destructors 
    Vec() { create(); } 
    explicit Vec(size_type n, const T& t = T()) { create(n, t); } 
    Vec(const Vec& v) { create(v.begin(), v.end()); } 
    ~Vec() { uncreate(); } 

    // methods 
    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); 
    } 

    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: 
    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&); 
}; 

#endif GUARD_Vec_h 

Vec.cpp

#include <algorithm> 
#include <cstddef> 
#include <memory> 
#include "Vec.h" 

using std::allocator; 
using std::max; 
using std::uninitialized_copy; 
using std::uninitialized_fill; 
using std::ptrdiff_t; 

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; 
    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 = 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 = 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); 
} 

爲什麼這個編譯不?

+3

我發現很難相信這本書沒有解釋爲什麼模板定義必須放在頭文件中......在任何情況下,閱讀[this](http://www.parashift.com/c++-faq) -lite/templates.html#FAQ-35.15)。 – ildjarn 2011-12-15 23:04:59

回答

0

問題出在任何和所有的模板函數上。當您編寫模板函數時,編譯器會實例化模板 - 即,它會使用運行代碼所需的類型創建特定版本的函數。所以當你打電話給

Vec<int> v; 

編譯器爲vec和你調用的那種類型的任何函數創建代碼。編譯器需要在爲主文件編寫代碼時訪問模板化的函數定義,因爲在鏈接其他文件之前,必須知道該目標文件的代碼類型。