Указатели, следующее значение

282
16 января 2017, 20:32

Вывод значений массива, получить 4 элемента по очереди, результат

1.000000  0.000000  -nan  -nan  0.000000 

отличается от ожидаемого

class DataSrc{
  int current_position;
  int dlina;
  double * curent_elem;
  public:
  DataSrc(){
      current_position=0;
      dlina=4;
      double histori[]={1, 2, 3, 7, 6, 2, 4, 3, 2};
      curent_elem=&histori[0];
  }  
  double * GetNext(){
     if(current_position>dlina){
         return 0;   
     }
     current_position++;
     return curent_elem+current_position-1;
  }  
};
int main(){
    DataSrc MyData;
    double *eee=MyData.GetNext();
    while(eee){
        printf(" %lf ",*eee);
        eee=MyData.GetNext();
    }
    return 0;
}

Как сделать правильно, почему популярный совет из учебников о получении следующего значения "адрес++" здесь не работает?

Answer 1

Потому что вы создали локальный массив histori, который после выхода из конструктора уничтожается, и храните указатель в фиг куда...

Если хочется, чтоб работало - то, например, так:

class DataSrc
{
    int current_position;
    int dlina;
    double * curent_elem;
    double * histori;
public:
    DataSrc()
    {
        histori = new double[9]{1, 2, 3, 7, 6, 2, 4, 3, 2};
        current_position = 0;
        dlina = 4;
        curent_elem = &histori[0];
    }
    ~DataSrc()
    {
        delete[] histori;
    }
    double * GetNext()
    {
        if(current_position>dlina) return 0;
        return curent_elem+current_position++;
    }
};
int main()
{
    DataSrc MyData;
    double *eee=MyData.GetNext();
    while(eee){
        printf(" %lf ",*eee);
        eee=MyData.GetNext();
    }
    return 0;
}

(вопрос о том, как написать так, чтобы не просто работало, но и имело смысл, пока не рассматриваем).

Answer 2

Как уже было отмечено, данное объявление в конструкторе класса

  DataSrc(){
      current_position=0;
      dlina=4;
      double histori[]={1, 2, 3, 7, 6, 2, 4, 3, 2};
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      curent_elem=&histori[0];
  }  

является объявлением локального массива с автоматической продолжительностью хранения. То есть время жизни этого массива завершается после выхода из конструктора. Поэтому обращение к этому массиву вне тела конструктора ведет к неопределенному поведению программы.

Чтобы иметь возможность обращаться периодически к некоторому массиву из методов класса, следует сделать этот массив членом класса.

У вас в классе имеется некоторая избыточность в объявлениях членов данных класса. Из этих двух членов класса

int current_position;
double * curent_elem;

достаточно иметь лишь один член класса current_position, так как второй член класса curent_elem всегда можно получить, имея первый член класса.

Инициализировать массив-член класса можно, передав конструктору указатель на первый элемент некоторого массива, используемого в качестве аргумента, и его размер. Либо можно в качестве параметра использовать список инициализации.

Также в минимальный набор методов класса желательно включить метод, который устанавливает в исходное значение член данных current_position, или проще его назвать как position.

Ниже приведен некоторый каркас класса, который можно развивать далее, наращивая его методы. В этом примере используется стандартный класс std::unique_ptr. Чем раньше вы начнете использовать стандартные средства из библиотеки C++, тем скорей вы к ним привыкните и освоите.

#include <iostream>
#include <memory>
#include <initializer_list>
#include <algorithm>
template <typename T>
class DataSrc
{
private:
    size_t n;
    std::unique_ptr<T[]> p;
    mutable size_t position;
public:
    DataSrc() : n( 0 ), p( nullptr ), position( 0 )
    {
    }
    DataSrc( const T *p, size_t n ) : n( n ), p( new T[n] ), position( 0 )
    {
        std::copy( p, p + n, DataSrc::p.get() );
    }
    DataSrc( std::initializer_list<T> lst  ) : n( lst.size() ), p( new T[lst.size()] ), position( 0 )
    {
        std::copy( lst.begin(), lst.end(), DataSrc::p.get() );
    }
    size_t size() const
    {
        return n;
    }
    const T * GetNext() const
    {
        return position == n ? nullptr : p.get() + position++;
    }
    T * GetNext()
    {
        return position == n ? nullptr : p.get() + position++;
    }
    void Reset()
    {
        position = 0;
    }
    void Reset() const
    {
        position = 0;
    }
};

int main() 
{
    int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, };
    DataSrc<int> data1( a, sizeof( a ) / sizeof( *a ) );
    while ( int *current = data1.GetNext() ) std::cout << *current << ' ';
    std::cout << std::endl;
    DataSrc<int> data2( { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, } );
    while ( int *current = data2.GetNext() ) std::cout << *current << ' ';
    std::cout << std::endl;
    data2.Reset();
    while ( int *current = data2.GetNext() ) std::cout << *current << ' ';
    std::cout << std::endl;
    return 0;
}

Вывод программы на консоль

0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9 

В этом примере класса не хватает конструкторов копирования и/или перемещения и, соответственно копирующего и/или перемещающего оператора присваивания. Вы можете попробовать написать их самостоятельно и, если возникнут трудности, задать соответствующий вопрос на форуме.

READ ALSO
Имеют ли смысл указатели на ф-ции класса?

Имеют ли смысл указатели на ф-ции класса?

Недавно столкнулся с одной непонятной для меня вещью - указатели на функции классаИмеют ли они смысл ? В каких случаях используются ? Пример:

344
Помогите создать SQL запрос

Помогите создать SQL запрос

Есть 2 таблицыparam_price:

324
Как взять названия колонок из значения колонки

Как взять названия колонок из значения колонки

Как в SQL-запросе подставить в название колонки значения, которые находятся в ней?

328
Экспорт данных из phpMyAdmin в Excel

Экспорт данных из phpMyAdmin в Excel

Как взять все данные из таблицы в БД и отправить их в Excel (сделать отчет)?

325