Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Конструктора копирования


Автор: Zhunko 30.11.2012, 02:18
Код

class A
 {
  int k;
  int n[2];
  public:
  A()
   {
    k = 0;
    n[0] = 123;
    n[1] = 345;
   }
  virtual ~A(){}
  A &operator[](int i)
   {
    k = i;
    return(*this);
   }
  A(const A &a){} // Без конструктора копирования работает правильно.
  operator int&()
   {
    return(n[k]);
   }
 };

template<class T> // Если закоментировать шаблон и жёстко задать тип int, работает правильно. Даже с конструктором копирования.
int MyFunc(T tIndex)
 {
  return(tIndex);
 }

void _tmain(int argc, LPTSTR argv[], LPTSTR envp[])
 {
    A a;
    std::vector<int> v(2, 123);
    v[1] = 456;
    tcout << a[0] << a[1] << ENDL; // Контрольный вывод для сравнения. Работает.
    tcout << MyFunc(a[0]) << MyFunc(a[1]) << ENDL; // Не работает!
    tcout << MyFunc<int>(a[0]) << MyFunc<int>(a[1]) << ENDL; // Так работает.
    tcout << MyFunc(v[0]) << MyFunc(v[1]) << ENDL; // Это всегда работало.
  }

В идеале два первых вывода должны быть одинаковы.
Но в случае наличия конструктора копирования работает неправильно. После вызова конструктора копирования оператор & не вызывается. Компилятор считает, что передаю класс А, а не число типа int.
В std::vector это каким-то образом сделано. Разбирался так и не понял. Там так всё просто, что зацепиться не за что.
Что надо сделать, чтобы конструктор копирования работал и оператор & после него вызывался?

Автор: math64 30.11.2012, 07:44
Ты объявил пустой коструктор копирования, который ничего не делает. естественно не работает.
Если коструктор копирования не объявлен, компилятор сделает его за тебя.
В данном случае сгенирированый компилятором конструктор будет правильным, и всё будет работать правильно.

Автор: Zhunko 30.11.2012, 11:56
Это для простоты восприятия таким показал конструктор копирования.
У меня в основном коде, откуда сделал упрощённый пример, такой:
Код

A(const A &a)
 {
   Init(a); // Полностью иициализирует класс.
 }

Это тоже не работает.
Тогда, какой должен быть конструктор копирования?

Автор: xvr 30.11.2012, 12:01
Цитата(Zhunko @  30.11.2012,  02:18 Найти цитируемый пост)
После вызова конструктора копирования оператор & не вызывается. 

У вас нет оператора &, а есть у вас оператор приведения типа к int& (и он скорее всего вызывается)


Автор: Zhunko 30.11.2012, 12:24
Цитата
После вызова конструктора копирования оператор & не вызывается.
У вас нет оператора &, а есть у вас оператор приведения типа к int& (и он скорее всего вызывается) 

Вызывается в случае отсутствия явного коструктора копирования. В остальных случаях игнорируется.

Автор: volatile 30.11.2012, 15:43
Цитата(Zhunko @  30.11.2012,  02:18 Найти цитируемый пост)
в случае наличия конструктора копирования работает неправильно

Прямой вывод => ваш конструктор копирования неверен. и это без вариантов.  smile 
как правильно сказал math64, 
Цитата(math64 @  30.11.2012,  07:44 Найти цитируемый пост)
Если коструктор копирования не объявлен, компилятор сделает его за тебя.

В программе всегда есть конструктор копирования, либо ваш, либо дефолтный.
С вашим не работает, с дефолтным работает ==> ...   что из этого следует ?

Хотя в коде дофига и другой путаницы.
например, непонятно зачем оператор [] возвращает сам объект.
но это уже второстепенно.

Добавлено через 7 минут и 27 секунд
вот, написал простеньки конст.коп. 
http://codepad.org/gd6BJTpF

что не работает ?

Автор: Zhunko 30.11.2012, 18:04
Спасибо! Ваш код работает. Нашёл чего не хватало у меня. Не все члены класса инициализировались. Как раз те, что отвечали за индекс, не добавил. Init() один раз написал и забыл.

Цитата(volatile @  30.11.2012,  16:43 Найти цитируемый пост)
Хотя в коде дофига и другой путаницы. например, непонятно зачем оператор [] возвращает сам объект.но это уже второстепенно.

На счёт возврата оператором [], спасибо! Глаз замылился. smile  Почему-то посчитал, что так лучше будет. Переделываю... 

Написал класс-оболочку над std::vector для работы с ним, как с многомерным динамическим массивом. Можно менять количество измерений на ходу без нарушения структуры данных. Есть возможность передавать указатель или ссылку на начало массива. Т.е. данные непрерывны. Тесты все прошёл, а, как попытался в качестве аргумента функции с шаблонным типом передать содержимое ячейки - ошибка.

Автор: borisbn 30.11.2012, 18:50
Цитата(Zhunko @  30.11.2012,  18:04 Найти цитируемый пост)
Написал класс-оболочку над std::vector для работы с ним, как с многомерным динамическим массивом.

буст переписывашь ?
http://www.boost.org/doc/libs/1_52_0/libs/multi_array/doc/user.html

Автор: Zhunko 30.11.2012, 19:21
Цитата(borisbn @  30.11.2012,  19:50 Найти цитируемый пост)
буст переписывашь ?

Буст смотрел. У меня круче и интерфейс проще. smile Буст не позволяет менять количество измерений. По моему, там нарушается структура данных при манипуляциях с размерами. У меня 64 перегрузки операторов для групповых операций с элементами массива. Есть копирование многомерный->многомерный с сохранением структуры данных. Куда покажешь - туда скопирует фрагмент массива.

Цитата(Zhunko @  30.11.2012,  19:04 Найти цитируемый пост)
На счёт возврата оператором [], спасибо! Глаз замылился.   Почему-то посчитал, что так лучше будет. Переделываю... 

Не вышло. Сначала правильно сделал. Это, если один оператор [] и он возвращает содержимое ячейки, то всё красиво выходит. Если много [], то не компилируется. Ему же требуется класс-объект, а не содержимое ячейки.

Сейчас всё работает. Спасибо всем! Особенно volatile!
Но последний вопрос остаётся.
Посмотрел в дебаггере. Всё же:
Код

tcout << MyFunc(a[0]) << MyFunc(a[1]) << ENDL;

a[0] функция получает, как класс A, а не число-значение (123) указанного типа int. Это потом срабатывает перегрузка
Код

operator int&(){}

и всё работает.
Как сделать так, чтобы компилятор сразу воспринимал аргумент, с типом содержимого ячейки массива?

Автор: volatile 1.12.2012, 00:27
Цитата(Zhunko @  30.11.2012,  19:21 Найти цитируемый пост)
Не вышло. Сначала правильно сделал. Это, если один оператор [] и он возвращает содержимое ячейки, то всё красиво выходит. Если много [], то не компилируется. Ему же требуется класс-объект, а не содержимое ячейки.

Вы вообще о чем? Вы серьезно считаете что здесь собрались исключительно экстрасенсы-телепаты ?

Цитата(Zhunko @  30.11.2012,  19:21 Найти цитируемый пост)
a[0] функция получает, как класс A, а не число-значение (123) указанного типа int ... Как сделать так, чтобы компилятор сразу воспринимал аргумент, с типом содержимого ячейки массива?

Вы возвращаете из оператора [] ссылку на А, компилятор и воспринимает ее как ссылку на А.
По моему, весьма логично. И что-то другое требовать от компилятора, как-то даже странно...

Цитата(Zhunko @  30.11.2012,  19:21 Найти цитируемый пост)
Буст смотрел. У меня круче и интерфейс проще.

Охотно верю, но по коду что вы привели, этого, мягко говоря, пока не скажешь.

Автор: baldina 1.12.2012, 00:39
Zhunko, буст конечно отстой по сравнению с вашим классом,  но я о другом хотел спросить: приведите плиз пример задачи, для решения которой нужен ваш класс, т.е. для чего нужен многомерный массив с изменяемыми размерностями?

Автор: volatile 1.12.2012, 00:54
Zhunko, и сформулируйте вопрос внятно, с кодом в котором можно понять проблему.
Не полагаясь на то, что все абсолютно в курсе всех подробностей того что вы там делаете.
и тогда вы возможно получите ответ.

у bsa есть хорошая фраза в подписи
Цитата

Правильно заданный вопрос - половина ответа



Автор: Zhunko 1.12.2012, 04:30
Цитата(volatile @  1.12.2012,  01:27 Найти цитируемый пост)
Вы вообще о чем? Вы серьезно считаете что здесь собрались исключительно экстрасенсы-телепаты ?

Да уж, яснее некуда объяснил. Имел ввиду несколько операторов [] подряд для такого обращения к многомерному массиву:
Код

int main ()
 {
    A a; // Для нетелепатов. Это не тот класс, что выше опубликован. Это пример нереализованного здесь класса многомерного массива.
    std::cout << a[0][1][2][3] << std::endl;
  }

Здесь компилятор требует к оператору [] массив или указатель. Так, что вернуть содержимое ячейки не получится для нескольких [].

Цитата(volatile @  1.12.2012,  01:27 Найти цитируемый пост)
Вы возвращаете из оператора [] ссылку на А, компилятор и воспринимает ее как ссылку на А.По моему, весьма логично. И что-то другое требовать от компилятора, как-то даже странно...

Ну нельзя, значит нельзя. Не настаиваю. smile 

Цитата(volatile @  1.12.2012,  01:27 Найти цитируемый пост)
Охотно верю, но по коду что вы привели, этого, мягко говоря, пока не скажешь.

Где код привёл?  smile  Ни строчки не выложил ещё. Всё, что здесь написал, это простые примеры демонстрации проблемы. Да и проблема-то ерундовая. Забыл пару переменных инициализировать.

Цитата(baldina @  1.12.2012,  01:39 Найти цитируемый пост)
Zhunko, буст конечно отстой по сравнению с вашим классом,  но я о другом хотел спросить: приведите плиз пример задачи, для решения которой нужен ваш класс, т.е. для чего нужен многомерный массив с изменяемыми размерностями?

Про отстой, заметьте, не я сказал  smile 
Есть такой язык MQL4. Там данные можно объединить только в массивы. Память, которой можно пользоваться, только недавно сделали 4 Гб. До этого было всего 2 Гб. Данные, которые требуется обрабатывать, составляют десятки Гб. На данный момент программный комплекс у меня обрабатывает максимум 5-мерный массив. Приходится по-всякому экономить. Хорошо, что ещё DLL разрешили подключать, а то совсем плохо было бы. В перспективе надо выводить расчёты за пределы MQL4. Тогда таких ограничений не будет. Но это непросто сейчас. Пока работает и так. Есть более важные задачи.

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

Цитата(volatile)

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

Нет проблемы. Нечего формулировать. Всё работает. Спасибо!

Автор: volatile 1.12.2012, 14:18
Цитата(Zhunko @  1.12.2012,  04:30 Найти цитируемый пост)
  std::cout << a[0][1][2][3] << std::endl;

В этой строчке, получается, у вас объект в правой стороне выражения изменяется 3 раза?
Поверьте, такой side-effect, мало кто, готов ожидать. 

Zhunko, я конечно не знаю что вы там пишите, но имхо, интутивно чувствую что вы делаете что-то не то.
например
Код

v = a[1] + a[2];
В этой строчке у вас объект (a) будет изменяться 2 раза, причем порядок изменения неопределен, по стандарту это УБ.
я уж не говорю про то, что он вообще в принципе никогда не может быть rvalue.
В общем, проблем на свою голову вы найдете...



Автор: Zhunko 1.12.2012, 16:23
Цитата(volatile @  1.12.2012,  15:18 Найти цитируемый пост)
Цитата(Zhunko @  1.12.2012,  04:30 )
Код

  std::cout << a[0][1][2][3] << std::endl;

В этой строчке, получается, у вас объект в правой стороне выражения изменяется 3 раза?
Поверьте, такой side-effect, мало кто, готов ожидать. 

4 раза будет меняться.

Цитата(volatile @  1.12.2012,  15:18 Найти цитируемый пост)
Zhunko, я конечно не знаю что вы там пишите, но имхо, интутивно чувствую что вы делаете что-то не то.например
Код

v = a[1] + a[2];

В этой строчке у вас объект (a) будет изменяться 2 раза, причем порядок изменения неопределен, по стандарту это УБ.я уж не говорю про то, что он вообще в принципе никогда не может быть rvalue.В общем, проблем на свою голову вы найдете...

К сожалению, такая арифметика для класса не доступна. Пока не знаю, как это сделать.
Правильнее сказать, знаю, как сделать некрасиво. Например, перегрузить [] с парой типов аргумента. Завершающий оператор вызывать с конкретным типом.
Лучше не буду так делать, чем иметь возможность складывать содержимое ячеек с индексацией в [].
Хотя, в этом что-то есть. Таким образом, можно частично на компилятор возложить слежение за правильностью применения []. Отслеживание количества [] сам класс делает.

Зато есть метод At(), с которым такая арифметика работает:
Код

v =  a.At(0, 1, 2, 3) + a.At(3, 2, 1, 0);

Автор: Zhunko 3.12.2012, 07:27
Цитата(volatile @  1.12.2012,  15:18 Найти цитируемый пост)
Zhunko, я конечно не знаю что вы там пишите, но имхо, интутивно чувствую что вы делаете что-то не то.например
Код

v = a[1] + a[2];

В этой строчке у вас объект (a) будет изменяться 2 раза, причем порядок изменения неопределен, по стандарту это УБ.я уж не говорю про то, что он вообще в принципе никогда не может быть rvalue.В общем, проблем на свою голову вы найдете...

Удалось сделать без дополнительных перегрузок [].
Достаточно перегрузки оператора приведения типа:
Код

  operator int&()
   {
    return(n[k]);
   }

Правда, лишился возможности перегрузить арифметические операторы для самого объекта. Теперь эти конструкции всегда возвращают содержимое ячейки даже без операторов [].
Но это не беда. Сам класс на базе std::vector сделан. Т.е. с ним, как с вектором можно обращаться. Есть перегуженные операторы для std::vector.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)