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

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

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

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

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

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

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;, предмет 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);

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

private:
double 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;
}

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

Normaldev структура из Числовых Рецептов 3d Edition.

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


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

typedef double Doub;
typedef unsigned long long int Ullong;
typedef unsigned int Uint;

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

struct Normaldev : Ranq1 {
Doub mu,sig;
Normaldev(Doub mmu, Doub ssig, Ullong i):
Ranq1(i), mu(mmu), sig(ssig){}
Doub dev() {
/* proprietary calculations here */
}
};

0

Решение

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

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 и seed. Ваш оператор присваивания имеет ту же проблему, что и должна быть

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

Я полагаю, вы так много внимания уделяли хитрому указателю в своем классе, что забыли об основных вещах.

Кстати код

MyClass A;
MyClass B = A;

на самом деле конвертируется компилятором в

MyClass A;
MyClass B(A);

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

3

Другие решения

Других решений пока нет …