c++ - 为什么显式定义拷贝构造函数后就无法通过编译了?
PHP中文网
PHP中文网 2017-04-17 14:59:27
0
4
666

初学C++,遇到了这样一个问题,代码如下:

#include <iostream>
using namespace std;

class Complex {
float real, imag;
public:
    Complex(float r = 0.0, float i = 0.0):real(r), imag(i) {
        cout << "Complex constructor called" << endl;
    }
    Complex (Complex& complex):real(complex.real), imag(complex.imag) {
        cout << "Complex copy constructor called" << endl;
    }

 int main() {
    Complex c3 = 3.14;
}

运行的时候会有编译错误,如下:

foobar.cpp:42:13: error: no viable constructor copying variable of
      type 'Complex'
    Complex c3 = 3.14;
            ^    ~~~~
foobar.cpp:10:5: note: candidate constructor not viable: expects an
      l-value for 1st argument
    Complex (Complex& complex):real(complex.real), imag(comp...
    ^

然而,当我删掉我显式定义的拷贝构造函数后,就可以正常编译了,即:

#include <iostream>
    using namespace std;
    
    class Complex {
    float real, imag;
    public:
        Complex(float r = 0.0, float i = 0.0):real(r), imag(i) {
            cout << "Complex constructor called" << endl;
        }
        

     int main() {
        Complex c3 = 3.14;
    }

这样就可以正常编译,请问这是为什么呢?
我用的是 g++ 4.2.1

目前我知道的是:当执行"Complex c3 = 3.14"时发生这些:先调用我显式定义的默认构造函数"Complex(float r = 3.14, float i = 0.0)"创建了一个临时对象,然后利用拷贝构造函数(如果有合法的)去创建c3,然后讲临时对象析构。

其实我的问题有两个:
1)为什么默认的拷贝构函数造能通过编译
2)为什么我定义的拷贝构函数造不能通过编译

补充:之前的问题已解决,但是现在遇到了新的问题
代码如下:

#include <iostream>
using namespace std;

class Complex {
    float real, imag;
public:
    Complex(float r = 0.0, float i = 0.0):real(r), imag(i) {
        cout << "Complex constructor called" << endl;
    }
    Complex(const Complex& complex):real(complex.real), imag(complex.imag) {
        cout << "Complex copy constructor called" << endl;
    }
};

int main() {
    Complex c = 3.22;
}

可以通过编译,但是输出结果为:

Complex constructor called   

并没有调用拷贝构造函数啊,为什么呢?是被编译器优化了吗?
如果不需要调用拷贝构造函数,那为什么之前不加const限定的拷贝构造函数不能通过编译呢?
我用的编译器是 g++ 4.2.1

PHP中文网
PHP中文网

认证高级PHP讲师

全員に返信(4)
PHPzhong

C++ 仕様により、現時点ではコンパイラーが不必要なコピー構築を最適化できるためです。
GCC とパラメーター -fno-elide-constructors を最適化する必要はありません。
ただし、コピー構築を呼び出すプロセスは後で最適化されました。ただし、コンパイルおよび構文チェックの際には、依然として存在する必要があります。 VC は直接最適化され、コピー コンストラクターをまったく呼び出しません。
最適化後、初期化式 Complex c3 = 3.14; は Complex c3(3.14); と同等になります。

いいねを押す +0
Peter_Zhu

Complex c3=3.14;このコピー コンストラクターは定数
なので、定義を Complex (const Complex& complex);
に変更する必要があります。 const キーワードを追加した後、非 const 変数もコピー構築できるため、一般的なコピーコンストラクターはすべて const を追加します。
これがお役に立てば幸いです!


ごめんなさい!私の以前の答えは、
Complex c3=3.14; が間違っていました。この 3.14 は複雑なオブジェクトではないため、システムはそれを float 型オブジェクトとして扱う必要があるため、その呼び出しは
Complex c3(3.14);
定義したコンストラクタ が呼び出されます。このプロセスは初期化であり、デフォルトのコピー コンストラクタは呼び出されません。

コピー コンストラクターは、同じクラスのオブジェクトがコピーされるときに呼び出されます。つまり、Complex オブジェクトがコピーされるときに呼び出されます。

例:
このときのみ、Complex c4=c3; はコピー コンストラクター を呼び出します。

以前のコードをコンピューターで実行したときにコンパイル エラーは見つかりませんでした。そのため、コンパイラーのせいだと思います。この点については詳しくないので、お答えできません。

私の答えがあなたのお役に立てば幸いです!

いいねを押す +0
小葫芦

コピー コンストラクターがクラスで提供されている場合、デフォルトのパラメーターなしおよびパラメーター化されたコンストラクターは提供されなくなるため、コピー コンストラクターを削除すると通常のコンパイルが可能になります

いいねを押す +0
迷茫

コードを投稿するときに中括弧とセミコロンを忘れた場合、これは異常なバグです...

Visual C++ 14.0 (VS2015) でコンパイルできます。

リーリー

実行結果:

リーリー

次に、あなたが追加した質問について:

リーリー

これが行うのは実際には初期化であり、代入ではありません。 C++ の父である Bjarne Stroustrup による「C++ ツアー」で次のように説明されています。

C++ では、上記で使用した = などの初期化を表現するためのさまざまな表記法や、中括弧で区切られた初期化子リストに基づく汎用形式が提供されています。

リーリー リーリー

= 形式は C に遡る伝統的な形式ですが、疑問がある場合は、一般的な {} リスト形式を使用してください。

いいねを押す +0
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート