5

私は自分自身にC++を教えようとしており、コンストラクターについての基本的な演習を行っています。予期しない動作をしているプログラムが1つあります。

Fraction.h:

#include <iostream>

#ifndef FRACTION_H
#define FRACTION_H

using namespace std;

class Fraction
{
private:
    int num;
    int denom;
    static int gcd(int a, int b);
    void reduce();
public:
    Fraction(int n=0, int d=1);
    Fraction(Fraction& f);
    ~Fraction();

    Fraction& operator=(const Fraction& f);

    friend Fraction operator+(const Fraction& f1, const Fraction& f2);

    friend ostream& operator<<(ostream& out, const Fraction& f);
};

#endif // FRACTION_H

Fraction.cpp(一部の実装は省略):

#include "../include/Fraction.h"

#include <cassert>
#include <iostream>

using namespace std;

int Fraction::gcd(int a, int b) {
    // implementation omitted
}

void Fraction::reduce() {
    // implementation omitted
    // this just reduces fractions to lowest terms, like 3/6 to 1/2
}

Fraction::Fraction(int n, int d) {
    cout << "new fraction, n=" << n << ", d=" << d << endl;
    assert(d != 0);
    if (d < 0) {
        num = -n;
        denom = -d;
    } else {
        num = n;
        denom = d;
    }
    reduce();
}

Fraction::Fraction(Fraction& f) {
    cout << "copy fraction " << f << " at " << &f << endl;
    num = f.num;
    denom = f.denom;
}

Fraction::~Fraction() {
}

Fraction& Fraction::operator=(const Fraction& f) {
    cout << "assign fraction to " << f << " at " << &f << endl;
    if (this == &f)
        return *this;
    num = f.num;
    denom = f.denom;
    return *this;
}

Fraction operator+(const Fraction& f1, const Fraction& f2) {
    cout << "adding " << f1 << " and " << f2 << endl;
    return Fraction(f1.num * f2.denom + f2.num * f1.denom,
                    f1.denom * f2.denom);
}

ostream& operator<<(ostream& out, const Fraction& f) {
    out << f.num << "/" << f.denom;
    return out;
}

main.cpp:

#include "include/Fraction.h"

#include <iostream>

using namespace std;

int main()
{
    Fraction f1(1, 3);
    Fraction f2(1, 2);
    cout << f1 << endl;
    cout << f2 << endl;
    cout << (f1 + f2) << endl;
    return 0;
}

これを実行すると、最初の2つのprintステートメントが期待どおりに出力1/3されます1/2が、3番目のステートメントは。0/1の代わりに出力され5/6ます。私が持っているデバッグステートメントから、Fraction(int, int)コンストラクターを介して5/6を作成しますが、何らかの理由で0/1で呼び出されます。コピーコンストラクターを削除すると、コードが出力され5/6ます。ここで何が起こっているのでしょうか。コピーコンストラクターを削除せずに修正するにはどうすればよいですか。

4

1 に答える 1

7

コピーコンストラクタの署名は次のようになります

Fraction(const Fraction&);

いいえ

Fraction(Fraction&);

を行うと、返される小数部が一時的なものであるためreturn Fraction(...);、コンパイラーは呼び出すFraction(const Fraction&)必要がありますが、定義しないため、コンパイラーは何か奇妙なことを起こします。コンパイラがおかしな動作をしていて、エラーが発生したときにデフォルトのコンストラクタをなんとかして使用できるようになっています。gccでコードをそのままコンパイルしても機能しません。前述の変更を加える必要があります。これで修正されます。

また、コンパイラがその関数でRVOを使用していないという事実は、非常に古いコンパイラや厄介なコンパイラを使用していることを示唆しています。

于 2012-04-08T23:48:30.907 に答える