Члены класса исказились после копирования или копирования по заданию (иногда)

Мой класс NRRanNormal представляет собой нормально распределенную случайную переменную. По умолчанию экземпляры обычно распределяются со средним значением 0 и stdev 1 (т.е. Стандартная нормальная случайная величина).

Иногда, когда я NRRanNormal объекты NRRanNormal, среднее и stdev объекта, скопированного в (или построенного с помощью конструктора копирования), искажены и бессмысленны. Мне трудно найти причину этого искажения.

Для целей тестирования следующая функция отображает среднее значение и stdev данного объекта NRRanNormal:

void go(NRRanNormal& rv, const string label) {
 std::cout << label << "\n"
 << "Mean: " << rv.getMean() << "\n"
 << "Stdev: " << rv.getStdev() << "\n\n";
}

Теперь посмотрим, что произойдет в следующих четырех случаях:

NRRanNormal foo;
go(foo, "foo");

NRRanNormal bar1 = foo;
go(bar1, "bar1");

NRRanNormal bar2;
bar2 = foo;
go(bar2, "bar2");

NRRanNormal bar3(foo);
go(bar3, "bar3");

Вывод приведенных выше инструкций таков:

foo
Mean: 0
Stdev: 1

bar1
Mean: 5.55633e-317
Stdev: 6.95332e-310

bar2
Mean: 0
Stdev: 1

bar3
Mean: 0
Stdev: 0

Как вы можете видеть, просто создание экземпляра объекта (foo) работает так, как ожидалось.

Теперь, когда я делаю NRRanNormal bar1 = foo; , объект bar1 искажен. Однако, когда я делаю NRRanNormal bar2; bar2 = foo; NRRanNormal bar2; bar2 = foo; , объект bar2 не искажен. Это меня озадачивает. Я думал, что блок заявления, такой как

MyClass A;
MyClass B = A;

фактически преобразуется компилятором в блок оператора

MyClass A;
MyClass B;
B = A;

Поэтому, если только то, что я только что написал сразу, неверно, кажется, что bar1 и bar2 должны иметь точно такие же значения элементов. Но, как видно из выведенного выше вывода, bar1 искажается, а bar2 - в порядке.

Как это может быть?

Вы также заметите, что bar3 искажен. Я не уверен, что это одна и та же проблема или другая проблема.

Вот упрощенная версия интерфейса и реализация NRRanNormal:

class NRRanNormal {
public:

 NRRanNormal();

 ~NRRanNormal();

 NRRanNormal(const NRRanNormal& nrran);

 NRRanNormal& operator= (const NRRanNormal& nrran);

 ****** getMean() const; 
 ****** getStdev() const; 
 long getSeed() const;

private: 
 ****** m_mean, m_stdev; 
 long m_seed; 
 Normaldev* stream; // underlying C struct RN generator

};

NRRanNormal::NRRanNormal() { // by default, N(0,1)
 m_mean = 0.0;
 m_stdev = 1.0;
 m_seed = 12345L;
 stream = new Normaldev(m_mean, m_stdev, m_seed);
}

NRRanNormal::~NRRanNormal() { delete stream; }

NRRanNormal::NRRanNormal(const NRRanNormal& nrran) {
 stream = new Normaldev(nrran.getMean(),nrran.getStdev(),nrran.getSeed());
 *stream = *(nrran.stream);
}

NRRanNormal& NRRanNormal::operator= (const NRRanNormal& nrran) { 
 if(this == &nrran)
 return *this;

 delete stream;
 stream = new Normaldev(nrran.getMean(),nrran.getStdev(),nrran.getSeed());
 *stream = *(nrran.stream);

 return *this;
}

****** NRRanNormal::getMean() const { return m_mean; } 
****** NRRanNormal::getStdev() const { return m_stdev; } 
long NRRanNormal::getSeed() const { return m_seed; }

Структура Normaldev - это Numerical Recipes 3d Edition.

Что-то не так с моим оператором присваивания копий или конструктором копирования?

Вот Normaldev, лишенный проприетарных вычислений.

typedef ****** ****; 
typedef unsigned long long int Ullong;
typedef unsigned int ****;

struct Ranq1 {
 Ullong v;
 Ranq1(Ullong j) : v(/* some long number here */) {
 /* proprietary calculations here */
 }
 inline Ullong int64() {
 /* proprietary calculations here */
 }
 inline **** ****() { /* proprietary calculations here */ }
 inline **** int32() { return (****)int64(); }
};

struct Normaldev : Ranq1 {
 **** mu,sig;
 Normaldev(**** mmu, **** ssig, Ullong i):
 Ranq1(i), mu(mmu), sig(ssig){}
 **** dev() {
 /* proprietary calculations here */
 }
};
1 ответ

Это ваша проблема

NRRanNormal::NRRanNormal(const NRRanNormal& nrran) {
 stream = new Normaldev(nrran.getMean(),nrran.getStdev(),nrran.getSeed());
 *stream = *(nrran.stream);
}

должно быть

NRRanNormal::NRRanNormal(const NRRanNormal& nrran) : 
 m_mean(nrran.m_mean), 
 m_stdev(nrran.m_stdev), 
 m_seed(nrran.m_seed)
{
 stream = new Normaldev(nrran.getMean(),nrran.getStdev(),nrran.getSeed());
 *stream = *(nrran.stream);
}

Ваш конструктор копирования не может скопировать среднее значение, stddev и семя. У вашего оператора присваивания та же проблема, что и

NRRanNormal& NRRanNormal::operator= (const NRRanNormal& nrran) { 
 if(this == &nrran)
 return *this;

 m_mean = nrran.m_mean;
 m_stdev = nrran.m_stdev;
 m_seed = nrran.m_seed;
 delete stream;
 stream = new Normaldev(nrran.getMean(),nrran.getStdev(),nrran.getSeed());
 *stream = *(nrran.stream);

 return *this;
}

Наверное, вы так сильно сосредоточились на сложном указателе в своем классе, о котором вы забыли о базовом материале.

BTW код

MyClass A;
MyClass B = A;

фактически преобразуется компилятором в

MyClass A;
MyClass B(A);

другими словами MyClass B = A; вызывает конструктор копирования (при условии, что A и B являются одним и тем же типом).

licensed under cc by-sa 3.0 with attribution.