2014-10-15 3 views
3

Я работаю над настраиваемым классом массива на C++ (как упражнение с самостоятельным управлением), и я не уверен, как создать конструктор, который позволяет мне что-то делать линии:Пользовательский класс массива: конструктор для инициализации списка

#include "array.h" 
#include <iostream> 
int main() 
{ 
    array<int> test = {1, 2, 3, 4}; 
    std::cout << test(1) << std::endl; 
    return 0; 
} 

ошибка, что компилятор (VS Экспресс 2013) дает мне «нет экземпляра конструктора массива :: массив [с T = INT]» совпадает со списком аргументов. Типы аргументов are (int, int, int, int). "

Я не уверен, что вызывается конструктором, который принимает перечисление набора элементов. Я знаю, что правильно перегрузил operator()(const int&). что это (по причине, что не ясно, для меня) работает:

#include "array.h" 
#include <iostream> 
int main() 
{ 
    array<char> test = "abcd"; 
    std::cout << test(1) << std:: endl; // prints 'a', as expected. 
    std::cout << test(4) << std::endl; // prints 'd', as expected. 
    return 0; 
} 

Это достигается с помощью array(const T[]) конструктора: будет решение для array<int> test = {1, 2, 3, ..., n} случае похоже

заранее спасибо за какие-либо указания?.

EDIT: Включая приведенный ниже код, в случае его полезности.

template<typename T> 
class array 
{ 
public: 
    typedef T* iterator; 
    typedef const T* const_iterator; 
private: 
    iterator head; 
    unsigned long elems; 
public: 
    array() 
     : head(nullptr) 
     , elems(0) {} 
    array(const unsigned long &size) 
     : head(size > 0 ? new T[size] : nullptr) 
     , elems(size) {} 
    array(const T[]); 
    array(const array&); 
    ~array() { delete[] head; } 

    iterator begin() const { return head; } 
    iterator end() const { return head != nullptr ? &head[elems] : nullptr; } 
    unsigned long size() const { return elems; } 

    array& operator=(const array&); 
    T& operator()(const unsigned long&); 
}; 

template<typename T> 
array<T>::array(const T rhs[]) 
{ 
    unsigned long size = sizeof(rhs)/sizeof(T); 
    head = new T[size]; 
    iterator pointer = begin(); 
    for (const_iterator i = &rhs[0]; i != &rhs[0] + size; i++) 
     *pointer++ = *i; 
} 

template<typename T> 
array<T>::array(const array<T> &rhs) 
{ 
    head = new T[rhs.size()]; 
    iterator pointer = begin(); 
    for (const_iterator i = rhs.begin(); i != rhs.end(); i++) 
     *pointer++ = *i; 
} 

template<typename T> 
array<T>& array<T>::operator=(const array<T> &rhs) 
{ 
    if (this != &rhs) 
    { 
     delete[] head; 
     head = new T[rhs.size()]; 
     iterator pointer = begin(); 
     for (const_iterator i = rhs.begin(); i != rhs.end(); i++) 
      *pointer++ = *i; 
    } 
    return *this; 
} 

template<typename T> 
T& array<T>::operator()(const unsigned long &index) 
{ 
    if (index < 1 || index > size()) 
    { 
     // Add some error-handling here. 
    } 
    return head[index - 1]; 
} 
+0

где ваш код шаблона класса массива? – taocp

+0

Это было явное упущение с моей стороны: извинения. Я включил его выше. – Corcovado

+0

'sizeof (rhs)' in 'array :: array (const T rhs [])' конечно же не делает то, что вы думаете. – WhozCraig

ответ

4
#include <initializer_list> 

// ... 

template <typename T> 
class array 
{ 
    // ... 
    array(std::initializer_list<T> il);  

// ...  
template <typename T> 
array<T>::array(std::initializer_list<T> il) 
{ 
    unsigned long size = il.size(); 
    head = new T[size]; 
    iterator pointer = begin(); 
    for (const T& i : il) 
     *pointer++ = i; 
} 

// ... 
array<int> test = {1, 2, 3, 4}; 

DEMO


Предлагаемые улучшения:

  1. array(const T rhs[]); является эквивалентом array(const T* rhs);, то есть указатель, который означает, что sizeof(rhs)/sizeof(T) выражение не будетдайте вам количество предметов. Если вы хотите специальный конструктор для const char*, а затем рассмотреть либо всю array<char> специализации или, по крайней мере, отключить этот конструктор от разрешения перегрузки, если T не char

  2. head = new T[size]; по умолчанию инициализирует все элементы (вызовы конструктора по умолчанию для каждого экземпляра тип T). Затем вы вызываете операцию назначения: *pointer++ = *i;. Это можно улучшить, используя размещение -new, например ::new ((void*)ptr) T(*i);, где ptr является указателем на необработанный, неинициализированный буфер памяти, например new char[sizeof(T)*size] или возвращен с get_temporary_buffer.


И если вы задаетесь вопросом, почему следующие работы array<char> test = { "abcd" }; с текущий реализации, то вот объяснение:

  1. array<T> класс имеет конструктор, принимающий const T[], который для T=char создается как array<char>::array(const char*).

  2. List-initialization может использоваться для вызова конструктора объекта.

  3. Ваш const T[] конструктор не явно, а это означает, что вы можете использовать копирования инициализацию синтаксис, как показано ниже:

    array<char> test = { "test" }; 
    
  4. Выражение sizeof(rhs)/sizeof(T), хотя и не действует, как описано выше, для T=char имеет значение sizeof(char*)/sizeof(char), который (наиболее вероятно) 4/1 = 4.

  5. Ваш "test", используемый для инициализации, имеет ровно 4 буквы, ваша удача.

+0

Спасибо за такой тщательный ответ, Петр. Один вопрос: как 'std :: initializer_list ' знать количество переданных аргументов? Разве это не пара указателей (http://en.cppreference.com/w/cpp/header/initializer_list)? Как создаются эти указатели, давая что-то вроде '{1, 2, 3, 4}'? – Corcovado

+0

Свой объект времени компиляции, компилятор подсчитывает их, использует функцию .size(), чтобы узнать, сколько их там. –

Смежные вопросы