关于C++拷贝构造函数的疑惑
阿神
阿神 2017-04-17 15:34:42
0
4
886

关于c++的拷贝构造函数.
书中写到:

  • 在C++中,下面三种对象需要调用拷贝构造函数!

    1. 对象以值传递的方式传入函数参数

    2. 对象以值传递的方式从函数返回

    3. 对象需要通过另外一个对象进行初始化;

针对上面的第二条,按照书中例子写出如下代码:

#include <iostream>
using namespace std;

class Point
{
public:
    Point(int xx = 0, int yy = 0) :x(xx),y(yy){}
    Point(Point &p){
        x = p.x;
        y = p.y;
        cout << "copy function is Called" << endl;
    }
    int GetX(){ return x; }
    int GetY(){ return y; }

private:
    int x, y;
};

Point fun()
{
    cout << "fun() Called\n";
    Point t(2, 2);
    return t;
}

int main()
{
      Point t1(1,1);
    Point t2 = fun();
    cout << t2.GetX()<< endl;;
}

以上代码能在visual studio 2013 环境 下顺利编译通过
输出为
fun() Called
copy function is Called
x is 2

当在g++环境下编译时,却出现了错误


C:\Users\Hale\Desktop\test1>g++ -std=c++11 test22.cpp -o test22
test22.cpp: In function 'int main()':
test22.cpp:30:17: error: no matching function for call to 'Point::Point(Point)'
  Point t2 = fun();
                 ^
test22.cpp:30:17: note: candidates are:
test22.cpp:8:2: note: Point::Point(Point&)
  Point(Point &p){
  ^
test22.cpp:8:2: note:   no known conversion for argument 1 from 'Point' to 'Point&'
test22.cpp:7:2: note: Point::Point(int, int)
  Point(int xx = 0, int yy = 0) :x(xx),y(yy){}
  ^
test22.cpp:7:2: note:   no known conversion for argument 1 from 'Point' to 'int'

C:\Users\Hale\Desktop\test1>g++ -std=c++11 test22.cpp -o test22
test22.cpp:8:15: error: invalid constructor; you probably meant 'Point (const Point&)'
  Point(Point p){
               ^

没有匹配的方法?于是添加构造函数

Point(const Point &p){
        x = p.x;
        y = p.y;
        cout << "const copy function is Called" << endl;
}

再次编译,成功!
结果如下

fun() Called
2

但是刚刚新增的构造方法的方法并没有被调用,但是结果是正确的。说好的作为函数返回对象会调用构造函数呢?
g++为什么会有这种处理?
书上未能找到相关答案,网上搜寻也没能获得满意答案!

阿神
阿神

闭关修行中......

全員に返信(4)
刘奇

質問 1

g++ 環境でコンパイルすると、エラーが発生しました: 一致するメソッドがありません?

エラーの理由は、コピー コンストラクターのパラメーターの型が正しく書かれておらず、const 修飾子が欠落していることです。 C++ では、一時変数を非 const 参照にバインドできません。元の投稿者のプログラム内の関数 Point fun() の戻り値は一時変数であり、コピー コンストラクター Point(Point &p) のパラメーターは非 const 参照であるため、コンパイラーは一致する関数がないことを示すエラーを報告します。 。コピー コンストラクターのパラメーターが定数参照に変更されると、一時変数を通常どおりバインドできるため、コンパイルは成功します。例:

リーリー

質問 2

新しく追加されたコンストラクター メソッドは呼び出されませんでしたが、結果は正しいです。関数からオブジェクトを返すときにコンストラクターが呼び出された場合はどうなるでしょうか?

これは、C++ 標準で、コンパイラが特定の状況下で関数の戻り値のコピー/移動操作を最適化できると明確に規定されているためです。

ISO C++11 標準 §8.5/16 では次の内容が規定されています (文脈は省略):

... 場合によっては、初期化されるオブジェクトに中間結果を直接構築することで、この直接初期化に固有のコピーを排除する実装が許可されます。12.2、12.8.

を参照してください。

§12.8/31 には次のようになります:

特定の基準が満たされる場合、オブジェクトのコピー/移動コンストラクターやデストラクターに副作用がある場合でも、実装はクラス オブジェクトのコピー/移動構築を省略できます。

つまり、これは単なるコンパイラの最適化です。作成者は、Visual Studio のリリース モードを使用してコンパイルを試みることができ、g++ のようなコピー コンストラクターをスキップする必要があります。

いいねを押す +0
小葫芦

関連情報を調べた結果、C++ 標準に [戻り値の最適化] があることがわかりました。
gcc/g++ がコピー コンストラクターを呼び出さない理由は、gcc/g++ が関連する操作を最適化し、不要なコピー操作を省略したためです。具体的な説明については、Zhihu で誰かが行った説明を参照してください:

  • GCC がこれを行います。構造体を返す必要がある関数の場合、呼び出し時に追加のアドレスが関数に渡されます。次に、関数は戻り値をこのアドレスに直接構築します。この利点は、追加のコピー構築を必要とせずに、戻り値が適切な場所に直接構築されることです。これは、不必要なコピー構築を回避する非常に賢い実装です。 GCC に高く評価してください! !
    著者: Li Qimai リンク: https://www.zhihu.com/questio... 出典: Zhihu の著作権は著者に属します。商業的転載の場合は、著者に連絡して許可を得てください。非商業的転載の場合は、出典を明記してください。

Visual Studio のデバッグ モードをリリースに変更すると、これらの不要な操作も最適化されていることがわかりました
Results
fun() Called
x は 2

これで問題は解決しました。

いいねを押す +0
小葫芦

私の記憶が正しければ、C++11 または C++14 は const なしで実行できるためです。以前は const が必要でしたが、実際にはこの制限は意味がありません。

いいねを押す +0
大家讲道理

実際にコピー コンストラクターを呼び出したのではなく、t2 に値を直接割り当てたため、次のようにコピー コンストラクターを使用する必要があります。

リーリー
いいねを押す +0
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!